From 8ff625ed61d7e8d1d4301ea86f8365871edbe25d Mon Sep 17 00:00:00 2001 From: Mathew May Date: Mon, 18 Jan 2021 11:40:23 +0800 Subject: [PATCH] MDL-70314 lib: Upgrade PhpSpreadsheet to 1.16.0 --- lib/phpspreadsheet/.DS_Store | Bin 0 -> 6148 bytes lib/phpspreadsheet/vendor/autoload.php | 2 +- .../vendor/composer/ClassLoader.php | 6 +- .../vendor/composer/InstalledVersions.php | 318 + .../vendor/composer/autoload_classmap.php | 1 + .../vendor/composer/autoload_files.php | 34 +- .../vendor/composer/autoload_namespaces.php | 1 + .../vendor/composer/autoload_psr4.php | 5 + .../vendor/composer/autoload_real.php | 21 +- .../vendor/composer/autoload_static.php | 88 +- .../vendor/composer/installed.json | 1082 ++- .../vendor/composer/installed.php | 123 + .../vendor/composer/platform_check.php | 26 + .../vendor/ezyang/htmlpurifier/CREDITS | 9 + .../vendor/ezyang/htmlpurifier/LICENSE | 504 ++ .../vendor/ezyang/htmlpurifier/README.md | 29 + .../vendor/ezyang/htmlpurifier/VERSION | 1 + .../vendor/ezyang/htmlpurifier/composer.json | 28 + .../library/HTMLPurifier.auto.php | 11 + .../library/HTMLPurifier.autoload-legacy.php | 15 + .../library/HTMLPurifier.autoload.php | 24 + .../library/HTMLPurifier.composer.php | 4 + .../library/HTMLPurifier.func.php | 25 + .../library/HTMLPurifier.includes.php | 234 + .../library/HTMLPurifier.kses.php | 30 + .../library/HTMLPurifier.path.php | 11 + .../htmlpurifier/library/HTMLPurifier.php | 297 + .../library/HTMLPurifier.safe-includes.php | 228 + .../library/HTMLPurifier/Arborize.php | 71 + .../library/HTMLPurifier/AttrCollections.php | 148 + .../library/HTMLPurifier/AttrDef.php | 144 + .../library/HTMLPurifier/AttrDef/CSS.php | 136 + .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 34 + .../HTMLPurifier/AttrDef/CSS/Background.php | 111 + .../AttrDef/CSS/BackgroundPosition.php | 157 + .../HTMLPurifier/AttrDef/CSS/Border.php | 56 + .../HTMLPurifier/AttrDef/CSS/Color.php | 161 + .../HTMLPurifier/AttrDef/CSS/Composite.php | 48 + .../AttrDef/CSS/DenyElementDecorator.php | 44 + .../HTMLPurifier/AttrDef/CSS/Filter.php | 77 + .../library/HTMLPurifier/AttrDef/CSS/Font.php | 176 + .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 219 + .../HTMLPurifier/AttrDef/CSS/Ident.php | 32 + .../AttrDef/CSS/ImportantDecorator.php | 56 + .../HTMLPurifier/AttrDef/CSS/Length.php | 77 + .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 112 + .../HTMLPurifier/AttrDef/CSS/Multiple.php | 71 + .../HTMLPurifier/AttrDef/CSS/Number.php | 90 + .../HTMLPurifier/AttrDef/CSS/Percentage.php | 54 + .../AttrDef/CSS/TextDecoration.php | 46 + .../library/HTMLPurifier/AttrDef/CSS/URI.php | 77 + .../library/HTMLPurifier/AttrDef/Clone.php | 44 + .../library/HTMLPurifier/AttrDef/Enum.php | 73 + .../HTMLPurifier/AttrDef/HTML/Bool.php | 48 + .../HTMLPurifier/AttrDef/HTML/Class.php | 48 + .../HTMLPurifier/AttrDef/HTML/Color.php | 51 + .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 38 + .../library/HTMLPurifier/AttrDef/HTML/ID.php | 113 + .../HTMLPurifier/AttrDef/HTML/Length.php | 56 + .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 72 + .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 60 + .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 70 + .../HTMLPurifier/AttrDef/HTML/Pixels.php | 76 + .../library/HTMLPurifier/AttrDef/Integer.php | 91 + .../library/HTMLPurifier/AttrDef/Lang.php | 86 + .../library/HTMLPurifier/AttrDef/Switch.php | 53 + .../library/HTMLPurifier/AttrDef/Text.php | 21 + .../library/HTMLPurifier/AttrDef/URI.php | 111 + .../HTMLPurifier/AttrDef/URI/Email.php | 20 + .../AttrDef/URI/Email/SimpleCheck.php | 29 + .../library/HTMLPurifier/AttrDef/URI/Host.php | 142 + .../library/HTMLPurifier/AttrDef/URI/IPv4.php | 45 + .../library/HTMLPurifier/AttrDef/URI/IPv6.php | 89 + .../library/HTMLPurifier/AttrTransform.php | 60 + .../HTMLPurifier/AttrTransform/Background.php | 28 + .../HTMLPurifier/AttrTransform/BdoDir.php | 27 + .../HTMLPurifier/AttrTransform/BgColor.php | 28 + .../HTMLPurifier/AttrTransform/BoolToCSS.php | 47 + .../HTMLPurifier/AttrTransform/Border.php | 26 + .../HTMLPurifier/AttrTransform/EnumToCSS.php | 68 + .../AttrTransform/ImgRequired.php | 47 + .../HTMLPurifier/AttrTransform/ImgSpace.php | 61 + .../HTMLPurifier/AttrTransform/Input.php | 56 + .../HTMLPurifier/AttrTransform/Lang.php | 31 + .../HTMLPurifier/AttrTransform/Length.php | 45 + .../HTMLPurifier/AttrTransform/Name.php | 33 + .../HTMLPurifier/AttrTransform/NameSync.php | 41 + .../HTMLPurifier/AttrTransform/Nofollow.php | 52 + .../HTMLPurifier/AttrTransform/SafeEmbed.php | 25 + .../HTMLPurifier/AttrTransform/SafeObject.php | 28 + .../HTMLPurifier/AttrTransform/SafeParam.php | 79 + .../AttrTransform/ScriptRequired.php | 23 + .../AttrTransform/TargetBlank.php | 45 + .../AttrTransform/TargetNoopener.php | 37 + .../AttrTransform/TargetNoreferrer.php | 37 + .../HTMLPurifier/AttrTransform/Textarea.php | 27 + .../library/HTMLPurifier/AttrTypes.php | 96 + .../library/HTMLPurifier/AttrValidator.php | 178 + .../library/HTMLPurifier/Bootstrap.php | 124 + .../library/HTMLPurifier/CSSDefinition.php | 533 ++ .../library/HTMLPurifier/ChildDef.php | 52 + .../HTMLPurifier/ChildDef/Chameleon.php | 67 + .../library/HTMLPurifier/ChildDef/Custom.php | 102 + .../library/HTMLPurifier/ChildDef/Empty.php | 38 + .../library/HTMLPurifier/ChildDef/List.php | 92 + .../HTMLPurifier/ChildDef/Optional.php | 45 + .../HTMLPurifier/ChildDef/Required.php | 118 + .../ChildDef/StrictBlockquote.php | 110 + .../library/HTMLPurifier/ChildDef/Table.php | 224 + .../library/HTMLPurifier/Config.php | 920 ++ .../library/HTMLPurifier/ConfigSchema.php | 176 + .../ConfigSchema/Builder/ConfigSchema.php | 48 + .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 144 + .../HTMLPurifier/ConfigSchema/Exception.php | 11 + .../HTMLPurifier/ConfigSchema/Interchange.php | 47 + .../ConfigSchema/Interchange/Directive.php | 89 + .../ConfigSchema/Interchange/Id.php | 58 + .../ConfigSchema/InterchangeBuilder.php | 226 + .../HTMLPurifier/ConfigSchema/Validator.php | 248 + .../ConfigSchema/ValidatorAtom.php | 130 + .../HTMLPurifier/ConfigSchema/schema.ser | 1 + .../schema/Attr.AllowedClasses.txt | 8 + .../schema/Attr.AllowedFrameTargets.txt | 12 + .../ConfigSchema/schema/Attr.AllowedRel.txt | 9 + .../ConfigSchema/schema/Attr.AllowedRev.txt | 9 + .../schema/Attr.ClassUseCDATA.txt | 19 + .../schema/Attr.DefaultImageAlt.txt | 11 + .../schema/Attr.DefaultInvalidImage.txt | 9 + .../schema/Attr.DefaultInvalidImageAlt.txt | 8 + .../schema/Attr.DefaultTextDir.txt | 10 + .../ConfigSchema/schema/Attr.EnableID.txt | 16 + .../schema/Attr.ForbiddenClasses.txt | 8 + .../ConfigSchema/schema/Attr.ID.HTML5.txt | 10 + .../ConfigSchema/schema/Attr.IDBlacklist.txt | 5 + .../schema/Attr.IDBlacklistRegexp.txt | 9 + .../ConfigSchema/schema/Attr.IDPrefix.txt | 12 + .../schema/Attr.IDPrefixLocal.txt | 14 + .../schema/AutoFormat.AutoParagraph.txt | 31 + .../ConfigSchema/schema/AutoFormat.Custom.txt | 12 + .../schema/AutoFormat.DisplayLinkURI.txt | 11 + .../schema/AutoFormat.Linkify.txt | 12 + .../AutoFormat.PurifierLinkify.DocURL.txt | 12 + .../schema/AutoFormat.PurifierLinkify.txt | 12 + .../AutoFormat.RemoveEmpty.Predicate.txt | 14 + ...rmat.RemoveEmpty.RemoveNbsp.Exceptions.txt | 11 + .../AutoFormat.RemoveEmpty.RemoveNbsp.txt | 15 + .../schema/AutoFormat.RemoveEmpty.txt | 46 + ...utoFormat.RemoveSpansWithoutAttributes.txt | 11 + .../schema/CSS.AllowDuplicates.txt | 11 + .../schema/CSS.AllowImportant.txt | 8 + .../ConfigSchema/schema/CSS.AllowTricky.txt | 11 + .../ConfigSchema/schema/CSS.AllowedFonts.txt | 12 + .../schema/CSS.AllowedProperties.txt | 18 + .../ConfigSchema/schema/CSS.DefinitionRev.txt | 11 + .../schema/CSS.ForbiddenProperties.txt | 13 + .../ConfigSchema/schema/CSS.MaxImgLength.txt | 16 + .../ConfigSchema/schema/CSS.Proprietary.txt | 10 + .../ConfigSchema/schema/CSS.Trusted.txt | 9 + .../schema/Cache.DefinitionImpl.txt | 14 + .../schema/Cache.SerializerPath.txt | 13 + .../schema/Cache.SerializerPermissions.txt | 16 + .../schema/Core.AggressivelyFixLt.txt | 18 + .../schema/Core.AggressivelyRemoveScript.txt | 16 + .../schema/Core.AllowHostnameUnderscore.txt | 16 + .../schema/Core.AllowParseManyTags.txt | 12 + .../schema/Core.CollectErrors.txt | 12 + .../schema/Core.ColorKeywords.txt | 160 + .../schema/Core.ConvertDocumentToFragment.txt | 14 + .../Core.DirectLexLineNumberSyncInterval.txt | 17 + .../schema/Core.DisableExcludes.txt | 14 + .../ConfigSchema/schema/Core.EnableIDNA.txt | 9 + .../ConfigSchema/schema/Core.Encoding.txt | 15 + .../schema/Core.EscapeInvalidChildren.txt | 12 + .../schema/Core.EscapeInvalidTags.txt | 7 + .../schema/Core.EscapeNonASCIICharacters.txt | 13 + .../schema/Core.HiddenElements.txt | 19 + .../ConfigSchema/schema/Core.Language.txt | 10 + .../schema/Core.LegacyEntityDecoder.txt | 36 + .../ConfigSchema/schema/Core.LexerImpl.txt | 34 + .../schema/Core.MaintainLineNumbers.txt | 16 + .../schema/Core.NormalizeNewlines.txt | 11 + .../schema/Core.RemoveInvalidImg.txt | 12 + .../Core.RemoveProcessingInstructions.txt | 11 + .../schema/Core.RemoveScriptContents.txt | 12 + .../ConfigSchema/schema/Filter.Custom.txt | 11 + .../Filter.ExtractStyleBlocks.Escaping.txt | 14 + .../Filter.ExtractStyleBlocks.Scope.txt | 29 + .../Filter.ExtractStyleBlocks.TidyImpl.txt | 16 + .../schema/Filter.ExtractStyleBlocks.txt | 74 + .../ConfigSchema/schema/Filter.YouTube.txt | 16 + .../ConfigSchema/schema/HTML.Allowed.txt | 25 + .../schema/HTML.AllowedAttributes.txt | 19 + .../schema/HTML.AllowedComments.txt | 10 + .../schema/HTML.AllowedCommentsRegexp.txt | 15 + .../schema/HTML.AllowedElements.txt | 23 + .../schema/HTML.AllowedModules.txt | 20 + .../schema/HTML.Attr.Name.UseCDATA.txt | 11 + .../ConfigSchema/schema/HTML.BlockWrapper.txt | 18 + .../ConfigSchema/schema/HTML.CoreModules.txt | 23 + .../schema/HTML.CustomDoctype.txt | 9 + .../ConfigSchema/schema/HTML.DefinitionID.txt | 33 + .../schema/HTML.DefinitionRev.txt | 16 + .../ConfigSchema/schema/HTML.Doctype.txt | 11 + .../schema/HTML.FlashAllowFullScreen.txt | 11 + .../schema/HTML.ForbiddenAttributes.txt | 21 + .../schema/HTML.ForbiddenElements.txt | 20 + .../ConfigSchema/schema/HTML.Forms.txt | 11 + .../ConfigSchema/schema/HTML.MaxImgLength.txt | 14 + .../ConfigSchema/schema/HTML.Nofollow.txt | 7 + .../ConfigSchema/schema/HTML.Parent.txt | 12 + .../ConfigSchema/schema/HTML.Proprietary.txt | 12 + .../ConfigSchema/schema/HTML.SafeEmbed.txt | 13 + .../ConfigSchema/schema/HTML.SafeIframe.txt | 13 + .../ConfigSchema/schema/HTML.SafeObject.txt | 13 + .../schema/HTML.SafeScripting.txt | 10 + .../ConfigSchema/schema/HTML.Strict.txt | 9 + .../ConfigSchema/schema/HTML.TargetBlank.txt | 8 + .../schema/HTML.TargetNoopener.txt | 10 + .../schema/HTML.TargetNoreferrer.txt | 9 + .../ConfigSchema/schema/HTML.TidyAdd.txt | 8 + .../ConfigSchema/schema/HTML.TidyLevel.txt | 24 + .../ConfigSchema/schema/HTML.TidyRemove.txt | 8 + .../ConfigSchema/schema/HTML.Trusted.txt | 9 + .../ConfigSchema/schema/HTML.XHTML.txt | 11 + .../schema/Output.CommentScriptContents.txt | 10 + .../schema/Output.FixInnerHTML.txt | 15 + .../schema/Output.FlashCompat.txt | 11 + .../ConfigSchema/schema/Output.Newline.txt | 13 + .../ConfigSchema/schema/Output.SortAttr.txt | 14 + .../ConfigSchema/schema/Output.TidyFormat.txt | 25 + .../ConfigSchema/schema/Test.ForceNoIconv.txt | 7 + .../schema/URI.AllowedSchemes.txt | 18 + .../ConfigSchema/schema/URI.Base.txt | 17 + .../ConfigSchema/schema/URI.DefaultScheme.txt | 15 + .../ConfigSchema/schema/URI.DefinitionID.txt | 11 + .../ConfigSchema/schema/URI.DefinitionRev.txt | 11 + .../ConfigSchema/schema/URI.Disable.txt | 14 + .../schema/URI.DisableExternal.txt | 11 + .../schema/URI.DisableExternalResources.txt | 13 + .../schema/URI.DisableResources.txt | 15 + .../ConfigSchema/schema/URI.Host.txt | 19 + .../ConfigSchema/schema/URI.HostBlacklist.txt | 9 + .../ConfigSchema/schema/URI.MakeAbsolute.txt | 13 + .../ConfigSchema/schema/URI.Munge.txt | 83 + .../schema/URI.MungeResources.txt | 17 + .../schema/URI.MungeSecretKey.txt | 30 + .../schema/URI.OverrideAllowedSchemes.txt | 9 + .../schema/URI.SafeIframeRegexp.txt | 22 + .../HTMLPurifier/ConfigSchema/schema/info.ini | 3 + .../library/HTMLPurifier/ContentSets.php | 170 + .../library/HTMLPurifier/Context.php | 95 + .../library/HTMLPurifier/Definition.php | 55 + .../library/HTMLPurifier/DefinitionCache.php | 129 + .../DefinitionCache/Decorator.php | 112 + .../DefinitionCache/Decorator/Cleanup.php | 78 + .../DefinitionCache/Decorator/Memory.php | 85 + .../DefinitionCache/Decorator/Template.php.in | 82 + .../HTMLPurifier/DefinitionCache/Null.php | 76 + .../DefinitionCache/Serializer.php | 311 + .../DefinitionCache/Serializer/README | 3 + .../HTMLPurifier/DefinitionCacheFactory.php | 106 + .../library/HTMLPurifier/Doctype.php | 73 + .../library/HTMLPurifier/DoctypeRegistry.php | 142 + .../library/HTMLPurifier/ElementDef.php | 216 + .../library/HTMLPurifier/Encoder.php | 617 ++ .../library/HTMLPurifier/EntityLookup.php | 48 + .../HTMLPurifier/EntityLookup/entities.ser | 1 + .../library/HTMLPurifier/EntityParser.php | 285 + .../library/HTMLPurifier/ErrorCollector.php | 244 + .../library/HTMLPurifier/ErrorStruct.php | 74 + .../library/HTMLPurifier/Exception.php | 12 + .../library/HTMLPurifier/Filter.php | 56 + .../Filter/ExtractStyleBlocks.php | 341 + .../library/HTMLPurifier/Filter/YouTube.php | 65 + .../library/HTMLPurifier/Generator.php | 286 + .../library/HTMLPurifier/HTMLDefinition.php | 493 + .../library/HTMLPurifier/HTMLModule.php | 284 + .../library/HTMLPurifier/HTMLModule/Bdo.php | 44 + .../HTMLModule/CommonAttributes.php | 31 + .../library/HTMLPurifier/HTMLModule/Edit.php | 55 + .../library/HTMLPurifier/HTMLModule/Forms.php | 194 + .../HTMLPurifier/HTMLModule/Hypertext.php | 40 + .../HTMLPurifier/HTMLModule/Iframe.php | 51 + .../library/HTMLPurifier/HTMLModule/Image.php | 49 + .../HTMLPurifier/HTMLModule/Legacy.php | 186 + .../library/HTMLPurifier/HTMLModule/List.php | 51 + .../library/HTMLPurifier/HTMLModule/Name.php | 26 + .../HTMLPurifier/HTMLModule/Nofollow.php | 25 + .../HTMLModule/NonXMLCommonAttributes.php | 20 + .../HTMLPurifier/HTMLModule/Object.php | 62 + .../HTMLPurifier/HTMLModule/Presentation.php | 42 + .../HTMLPurifier/HTMLModule/Proprietary.php | 40 + .../library/HTMLPurifier/HTMLModule/Ruby.php | 36 + .../HTMLPurifier/HTMLModule/SafeEmbed.php | 40 + .../HTMLPurifier/HTMLModule/SafeObject.php | 62 + .../HTMLPurifier/HTMLModule/SafeScripting.php | 40 + .../HTMLPurifier/HTMLModule/Scripting.php | 73 + .../HTMLModule/StyleAttribute.php | 33 + .../HTMLPurifier/HTMLModule/Tables.php | 75 + .../HTMLPurifier/HTMLModule/Target.php | 28 + .../HTMLPurifier/HTMLModule/TargetBlank.php | 24 + .../HTMLModule/TargetNoopener.php | 21 + .../HTMLModule/TargetNoreferrer.php | 21 + .../library/HTMLPurifier/HTMLModule/Text.php | 87 + .../library/HTMLPurifier/HTMLModule/Tidy.php | 230 + .../HTMLPurifier/HTMLModule/Tidy/Name.php | 33 + .../HTMLModule/Tidy/Proprietary.php | 34 + .../HTMLPurifier/HTMLModule/Tidy/Strict.php | 43 + .../HTMLModule/Tidy/Transitional.php | 16 + .../HTMLPurifier/HTMLModule/Tidy/XHTML.php | 26 + .../HTMLModule/Tidy/XHTMLAndHTML4.php | 180 + .../HTMLModule/XMLCommonAttributes.php | 20 + .../HTMLPurifier/HTMLModuleManager.php | 467 + .../library/HTMLPurifier/IDAccumulator.php | 57 + .../library/HTMLPurifier/Injector.php | 283 + .../HTMLPurifier/Injector/AutoParagraph.php | 356 + .../HTMLPurifier/Injector/DisplayLinkURI.php | 40 + .../library/HTMLPurifier/Injector/Linkify.php | 64 + .../HTMLPurifier/Injector/PurifierLinkify.php | 71 + .../HTMLPurifier/Injector/RemoveEmpty.php | 112 + .../Injector/RemoveSpansWithoutAttributes.php | 84 + .../HTMLPurifier/Injector/SafeObject.php | 124 + .../library/HTMLPurifier/Language.php | 204 + .../HTMLPurifier/Language/messages/en.php | 55 + .../library/HTMLPurifier/LanguageFactory.php | 209 + .../library/HTMLPurifier/Length.php | 162 + .../library/HTMLPurifier/Lexer.php | 382 + .../library/HTMLPurifier/Lexer/DOMLex.php | 338 + .../library/HTMLPurifier/Lexer/DirectLex.php | 539 ++ .../library/HTMLPurifier/Lexer/PH5P.php | 4788 ++++++++++ .../library/HTMLPurifier/Node.php | 49 + .../library/HTMLPurifier/Node/Comment.php | 36 + .../library/HTMLPurifier/Node/Element.php | 59 + .../library/HTMLPurifier/Node/Text.php | 54 + .../library/HTMLPurifier/PercentEncoder.php | 111 + .../library/HTMLPurifier/Printer.php | 218 + .../HTMLPurifier/Printer/CSSDefinition.php | 44 + .../HTMLPurifier/Printer/ConfigForm.css | 10 + .../HTMLPurifier/Printer/ConfigForm.js | 5 + .../HTMLPurifier/Printer/ConfigForm.php | 451 + .../HTMLPurifier/Printer/HTMLDefinition.php | 324 + .../library/HTMLPurifier/PropertyList.php | 122 + .../HTMLPurifier/PropertyListIterator.php | 42 + .../library/HTMLPurifier/Queue.php | 56 + .../library/HTMLPurifier/Strategy.php | 26 + .../HTMLPurifier/Strategy/Composite.php | 30 + .../library/HTMLPurifier/Strategy/Core.php | 17 + .../HTMLPurifier/Strategy/FixNesting.php | 181 + .../HTMLPurifier/Strategy/MakeWellFormed.php | 659 ++ .../Strategy/RemoveForeignElements.php | 207 + .../Strategy/ValidateAttributes.php | 45 + .../library/HTMLPurifier/StringHash.php | 47 + .../library/HTMLPurifier/StringHashParser.php | 136 + .../library/HTMLPurifier/TagTransform.php | 37 + .../HTMLPurifier/TagTransform/Font.php | 114 + .../HTMLPurifier/TagTransform/Simple.php | 44 + .../library/HTMLPurifier/Token.php | 100 + .../library/HTMLPurifier/Token/Comment.php | 38 + .../library/HTMLPurifier/Token/Empty.php | 15 + .../library/HTMLPurifier/Token/End.php | 24 + .../library/HTMLPurifier/Token/Start.php | 10 + .../library/HTMLPurifier/Token/Tag.php | 68 + .../library/HTMLPurifier/Token/Text.php | 53 + .../library/HTMLPurifier/TokenFactory.php | 118 + .../htmlpurifier/library/HTMLPurifier/URI.php | 316 + .../library/HTMLPurifier/URIDefinition.php | 112 + .../library/HTMLPurifier/URIFilter.php | 74 + .../URIFilter/DisableExternal.php | 54 + .../URIFilter/DisableExternalResources.php | 25 + .../URIFilter/DisableResources.php | 22 + .../HTMLPurifier/URIFilter/HostBlacklist.php | 46 + .../HTMLPurifier/URIFilter/MakeAbsolute.php | 158 + .../library/HTMLPurifier/URIFilter/Munge.php | 115 + .../HTMLPurifier/URIFilter/SafeIframe.php | 68 + .../library/HTMLPurifier/URIParser.php | 71 + .../library/HTMLPurifier/URIScheme.php | 102 + .../library/HTMLPurifier/URIScheme/data.php | 136 + .../library/HTMLPurifier/URIScheme/file.php | 44 + .../library/HTMLPurifier/URIScheme/ftp.php | 58 + .../library/HTMLPurifier/URIScheme/http.php | 36 + .../library/HTMLPurifier/URIScheme/https.php | 18 + .../library/HTMLPurifier/URIScheme/mailto.php | 40 + .../library/HTMLPurifier/URIScheme/news.php | 35 + .../library/HTMLPurifier/URIScheme/nntp.php | 32 + .../library/HTMLPurifier/URIScheme/tel.php | 46 + .../HTMLPurifier/URISchemeRegistry.php | 81 + .../library/HTMLPurifier/UnitConverter.php | 307 + .../library/HTMLPurifier/VarParser.php | 198 + .../HTMLPurifier/VarParser/Flexible.php | 130 + .../library/HTMLPurifier/VarParser/Native.php | 38 + .../HTMLPurifier/VarParserException.php | 11 + .../library/HTMLPurifier/Zipper.php | 157 + .../zipstream-php/.github/FUNDING.yml | 1 + .../zipstream-php/.github/ISSUE_TEMPLATE.md | 12 + .../vendor/maennchen/zipstream-php/.gitignore | 6 + .../maennchen/zipstream-php/.travis.yml | 12 + .../maennchen/zipstream-php/CHANGELOG.md | 51 + .../maennchen/zipstream-php/CONTRIBUTING.md | 25 + .../vendor/maennchen/zipstream-php/LICENSE | 24 + .../vendor/maennchen/zipstream-php/README.md | 123 + .../maennchen/zipstream-php/composer.json | 41 + .../maennchen/zipstream-php/phpunit.xml.dist | 17 + .../vendor/maennchen/zipstream-php/psalm.xml | 55 + .../maennchen/zipstream-php/src/Bigint.php | 172 + .../zipstream-php/src/DeflateStream.php | 70 + .../maennchen/zipstream-php/src/Exception.php | 11 + .../src/Exception/EncodingException.php | 13 + .../src/Exception/FileNotFoundException.php | 22 + .../Exception/FileNotReadableException.php | 22 + .../IncompatibleOptionsException.php | 13 + .../src/Exception/OverflowException.php | 17 + .../Exception/StreamNotReadableException.php | 22 + .../maennchen/zipstream-php/src/File.php | 477 + .../zipstream-php/src/Option/Archive.php | 261 + .../zipstream-php/src/Option/File.php | 116 + .../zipstream-php/src/Option/Method.php | 19 + .../zipstream-php/src/Option/Version.php | 22 + .../maennchen/zipstream-php/src/Stream.php | 253 + .../maennchen/zipstream-php/src/ZipStream.php | 599 ++ .../zipstream-php/test/BigintTest.php | 65 + .../zipstream-php/test/ZipStreamTest.php | 586 ++ .../zipstream-php/test/bootstrap.php | 6 + .../test/bug/BugHonorFileTimeTest.php | 39 + .../markbaker/complex/classes/Autoloader.php | 2 +- .../markbaker/complex/classes/src/Complex.php | 33 +- .../complex/classes/src/functions/abs.php | 2 +- .../complex/classes/src/functions/acos.php | 2 +- .../complex/classes/src/functions/acot.php | 2 +- .../complex/classes/src/functions/acoth.php | 2 +- .../complex/classes/src/functions/acsc.php | 4 +- .../complex/classes/src/functions/acsch.php | 4 +- .../classes/src/functions/argument.php | 2 +- .../complex/classes/src/functions/asec.php | 4 +- .../complex/classes/src/functions/asech.php | 4 +- .../complex/classes/src/functions/asin.php | 2 +- .../complex/classes/src/functions/asinh.php | 2 +- .../complex/classes/src/functions/atan.php | 2 +- .../complex/classes/src/functions/atanh.php | 2 +- .../classes/src/functions/conjugate.php | 2 +- .../complex/classes/src/functions/cos.php | 2 +- .../complex/classes/src/functions/cosh.php | 2 +- .../complex/classes/src/functions/cot.php | 2 +- .../complex/classes/src/functions/coth.php | 2 +- .../complex/classes/src/functions/csc.php | 4 +- .../complex/classes/src/functions/csch.php | 4 +- .../complex/classes/src/functions/exp.php | 2 +- .../complex/classes/src/functions/inverse.php | 2 +- .../complex/classes/src/functions/ln.php | 2 +- .../complex/classes/src/functions/log10.php | 2 +- .../complex/classes/src/functions/log2.php | 2 +- .../classes/src/functions/negative.php | 4 +- .../complex/classes/src/functions/pow.php | 2 +- .../complex/classes/src/functions/rho.php | 2 +- .../complex/classes/src/functions/sec.php | 2 +- .../complex/classes/src/functions/sech.php | 2 +- .../complex/classes/src/functions/sin.php | 2 +- .../complex/classes/src/functions/sinh.php | 2 +- .../complex/classes/src/functions/sqrt.php | 2 +- .../complex/classes/src/functions/tan.php | 2 +- .../complex/classes/src/functions/tanh.php | 2 +- .../complex/classes/src/functions/theta.php | 2 +- .../complex/classes/src/operations/add.php | 2 +- .../classes/src/operations/divideby.php | 2 +- .../classes/src/operations/divideinto.php | 2 +- .../classes/src/operations/multiply.php | 2 +- .../classes/src/operations/subtract.php | 2 +- .../vendor/markbaker/complex/composer.json | 35 +- .../markbaker/matrix/classes/src/Matrix.php | 36 +- .../matrix/classes/src/Operators/Addition.php | 6 +- .../classes/src/Operators/DirectSum.php | 4 +- .../matrix/classes/src/Operators/Division.php | 2 +- .../classes/src/Operators/Multiplication.php | 6 +- .../matrix/classes/src/Operators/Operator.php | 6 +- .../classes/src/Operators/Subtraction.php | 6 +- .../matrix/classes/src/functions/adjoint.php | 2 +- .../classes/src/functions/antidiagonal.php | 2 +- .../classes/src/functions/cofactors.php | 2 +- .../classes/src/functions/determinant.php | 2 +- .../matrix/classes/src/functions/diagonal.php | 2 +- .../matrix/classes/src/functions/identity.php | 2 +- .../matrix/classes/src/functions/inverse.php | 2 +- .../matrix/classes/src/functions/minors.php | 2 +- .../matrix/classes/src/functions/trace.php | 2 +- .../classes/src/functions/transpose.php | 2 +- .../matrix/classes/src/operations/add.php | 2 +- .../classes/src/operations/directsum.php | 2 +- .../classes/src/operations/divideby.php | 2 +- .../classes/src/operations/divideinto.php | 2 +- .../classes/src/operations/multiply.php | 2 +- .../classes/src/operations/subtract.php | 2 +- .../vendor/markbaker/matrix/composer.7.2.json | 86 - .../vendor/markbaker/matrix/composer.json | 24 +- .../markbaker/matrix/infection.json.dist | 17 + .../vendor/markbaker/matrix/phpstan.neon | 5 + .../vendor/myclabs/php-enum/LICENSE | 18 + .../vendor/myclabs/php-enum/README.md | 134 + .../vendor/myclabs/php-enum/SECURITY.md | 11 + .../vendor/myclabs/php-enum/composer.json | 33 + .../vendor/myclabs/php-enum/psalm.xml | 20 + .../vendor/myclabs/php-enum/src/Enum.php | 250 + .../php-enum/src/PHPUnit/Comparator.php | 54 + .../phpoffice/phpspreadsheet/.phpcs.xml.dist | 22 + .../phpspreadsheet/CHANGELOG.PHPExcel.md | 1593 ---- .../phpoffice/phpspreadsheet/CHANGELOG.md | 251 +- .../vendor/phpoffice/phpspreadsheet/README.md | 30 + .../phpoffice/phpspreadsheet/composer.json | 24 +- .../phpoffice/phpspreadsheet/composer.lock | 3503 -------- .../phpoffice/phpspreadsheet/mkdocs.yml | 7 - .../phpoffice/phpspreadsheet/phpunit.xml.dist | 23 - .../phpspreadsheet/src/Bootstrap.php | 22 - .../Calculation/Calculation.php | 907 +- .../PhpSpreadsheet/Calculation/Category.php | 1 + .../PhpSpreadsheet/Calculation/Database.php | 28 +- .../PhpSpreadsheet/Calculation/DateTime.php | 212 +- .../Engine/CyclicReferenceStack.php | 4 +- .../Calculation/Engine/Logger.php | 26 +- .../Calculation/Engineering.php | 924 +- .../Calculation/Engineering/ConvertUOM.php | 684 ++ .../PhpSpreadsheet/Calculation/Exception.php | 2 +- .../PhpSpreadsheet/Calculation/Financial.php | 524 +- .../Calculation/FormulaParser.php | 42 +- .../Calculation/FormulaToken.php | 6 +- .../PhpSpreadsheet/Calculation/Functions.php | 36 +- .../PhpSpreadsheet/Calculation/Logical.php | 55 +- .../PhpSpreadsheet/Calculation/LookupRef.php | 82 +- .../PhpSpreadsheet/Calculation/MathTrig.php | 380 +- .../Calculation/Statistical.php | 507 +- .../PhpSpreadsheet/Calculation/TextData.php | 26 +- .../Calculation/Token/Stack.php | 4 +- .../src/PhpSpreadsheet/Calculation/Web.php | 53 + .../Calculation/functionlist.txt | 7 + .../src/PhpSpreadsheet/Cell/AddressHelper.php | 134 + .../Cell/AdvancedValueBinder.php | 2 - .../src/PhpSpreadsheet/Cell/Cell.php | 50 +- .../src/PhpSpreadsheet/Cell/Coordinate.php | 83 +- .../PhpSpreadsheet/Cell/DataValidation.php | 26 +- .../Cell/DefaultValueBinder.php | 4 +- .../src/PhpSpreadsheet/Cell/Hyperlink.php | 4 +- .../PhpSpreadsheet/Cell/StringValueBinder.php | 2 - .../src/PhpSpreadsheet/Chart/Axis.php | 33 +- .../src/PhpSpreadsheet/Chart/Chart.php | 51 +- .../src/PhpSpreadsheet/Chart/DataSeries.php | 16 +- .../PhpSpreadsheet/Chart/DataSeriesValues.php | 18 +- .../src/PhpSpreadsheet/Chart/GridLines.php | 29 +- .../src/PhpSpreadsheet/Chart/Layout.php | 30 +- .../src/PhpSpreadsheet/Chart/Legend.php | 3 +- .../src/PhpSpreadsheet/Chart/PlotArea.php | 7 +- .../Chart/Renderer/IRenderer.php | 2 - .../PhpSpreadsheet/Chart/Renderer/JpGraph.php | 100 +- .../Chart/Renderer/Polyfill.php | 9 - .../src/PhpSpreadsheet/Chart/Title.php | 5 +- .../src/PhpSpreadsheet/Collection/Cells.php | 24 +- .../src/PhpSpreadsheet/Comment.php | 18 +- .../src/PhpSpreadsheet/DefinedName.php | 263 + .../PhpSpreadsheet/Document/Properties.php | 37 +- .../src/PhpSpreadsheet/Document/Security.php | 10 +- .../src/PhpSpreadsheet/DocumentGenerator.php | 97 + .../src/PhpSpreadsheet/HashTable.php | 12 +- .../src/PhpSpreadsheet/Helper/Html.php | 52 +- .../src/PhpSpreadsheet/Helper/Migrator.php | 333 - .../src/PhpSpreadsheet/Helper/Sample.php | 15 +- .../src/PhpSpreadsheet/IOFactory.php | 15 +- .../src/PhpSpreadsheet/NamedFormula.php | 45 + .../src/PhpSpreadsheet/NamedRange.php | 235 +- .../src/PhpSpreadsheet/Reader/BaseReader.php | 27 +- .../src/PhpSpreadsheet/Reader/Csv.php | 239 +- .../src/PhpSpreadsheet/Reader/Gnumeric.php | 1114 ++- .../Reader/Gnumeric/PageSetup.php | 143 + .../src/PhpSpreadsheet/Reader/Html.php | 723 +- .../src/PhpSpreadsheet/Reader/IReader.php | 4 - .../src/PhpSpreadsheet/Reader/Ods.php | 229 +- .../Reader/Ods/PageSettings.php | 139 + .../PhpSpreadsheet/Reader/Ods/Properties.php | 17 +- .../Reader/Security/XmlScanner.php | 14 +- .../src/PhpSpreadsheet/Reader/Slk.php | 617 +- .../src/PhpSpreadsheet/Reader/Xls.php | 7977 +++++++++++++++++ .../src/PhpSpreadsheet/Reader/Xls/Color.php | 36 + .../PhpSpreadsheet/Reader/Xls/Color/BIFF5.php | 81 + .../PhpSpreadsheet/Reader/Xls/Color/BIFF8.php | 81 + .../Reader/Xls/Color/BuiltIn.php | 35 + .../PhpSpreadsheet/Reader/Xls/ErrorCode.php | 32 + .../src/PhpSpreadsheet/Reader/Xls/Escher.php | 677 ++ .../src/PhpSpreadsheet/Reader/Xls/MD5.php | 184 + .../src/PhpSpreadsheet/Reader/Xls/RC4.php | 61 + .../Reader/Xls/Style/Border.php | 42 + .../Reader/Xls/Style/FillPattern.php | 47 + .../src/PhpSpreadsheet/Reader/Xlsx.php | 166 +- .../PhpSpreadsheet/Reader/Xlsx/AutoFilter.php | 18 +- .../src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 5 +- .../Reader/Xlsx/ColumnAndRowAttributes.php | 27 +- .../Reader/Xlsx/ConditionalStyles.php | 15 +- .../Reader/Xlsx/DataValidations.php | 5 +- .../PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php | 9 +- .../PhpSpreadsheet/Reader/Xlsx/PageSetup.php | 48 +- .../PhpSpreadsheet/Reader/Xlsx/Properties.php | 9 +- .../Reader/Xlsx/SheetViewOptions.php | 47 +- .../PhpSpreadsheet/Reader/Xlsx/SheetViews.php | 21 +- .../src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 41 +- .../src/PhpSpreadsheet/Reader/Xml.php | 410 +- .../Reader/Xml/PageSettings.php | 130 + .../src/PhpSpreadsheet/ReferenceHelper.php | 194 +- .../src/PhpSpreadsheet/RichText/RichText.php | 13 +- .../src/PhpSpreadsheet/RichText/Run.php | 4 +- .../PhpSpreadsheet/RichText/TextElement.php | 2 +- .../src/PhpSpreadsheet/Settings.php | 75 +- .../src/PhpSpreadsheet/Shared/CodePage.php | 193 +- .../src/PhpSpreadsheet/Shared/Date.php | 55 +- .../src/PhpSpreadsheet/Shared/Drawing.php | 8 +- .../Shared/Escher/DgContainer.php | 4 +- .../Escher/DgContainer/SpgrContainer.php | 4 +- .../DgContainer/SpgrContainer/SpContainer.php | 24 +- .../Shared/Escher/DggContainer.php | 12 +- .../Escher/DggContainer/BstoreContainer.php | 2 +- .../DggContainer/BstoreContainer/BSE.php | 6 +- .../DggContainer/BstoreContainer/BSE/Blip.php | 4 +- .../src/PhpSpreadsheet/Shared/File.php | 12 +- .../src/PhpSpreadsheet/Shared/Font.php | 11 +- .../Shared/JAMA/EigenvalueDecomposition.php | 26 +- .../Shared/JAMA/LUDecomposition.php | 3 - .../src/PhpSpreadsheet/Shared/JAMA/Matrix.php | 43 +- .../Shared/JAMA/QRDecomposition.php | 2 +- .../JAMA/SingularValueDecomposition.php | 14 +- .../Shared/JAMA/utils/Maths.php | 1 + .../src/PhpSpreadsheet/Shared/OLE.php | 566 ++ .../Shared/OLE/ChainedBlockStream.php | 196 + .../src/PhpSpreadsheet/Shared/OLE/PPS.php | 237 + .../PhpSpreadsheet/Shared/OLE/PPS/File.php | 64 + .../PhpSpreadsheet/Shared/OLE/PPS/Root.php | 426 + .../src/PhpSpreadsheet/Shared/OLERead.php | 350 + .../PhpSpreadsheet/Shared/PasswordHasher.php | 69 +- .../PhpSpreadsheet/Shared/StringHelper.php | 12 +- .../src/PhpSpreadsheet/Shared/TimeZone.php | 8 +- .../PhpSpreadsheet/Shared/Trend/BestFit.php | 6 +- .../Shared/Trend/ExponentialBestFit.php | 4 +- .../Shared/Trend/LinearBestFit.php | 2 +- .../Shared/Trend/LogarithmicBestFit.php | 2 +- .../Shared/Trend/PolynomialBestFit.php | 8 +- .../Shared/Trend/PowerBestFit.php | 6 +- .../src/PhpSpreadsheet/Shared/Xls.php | 279 + .../src/PhpSpreadsheet/Spreadsheet.php | 317 +- .../src/PhpSpreadsheet/Style/Alignment.php | 50 +- .../src/PhpSpreadsheet/Style/Border.php | 33 +- .../src/PhpSpreadsheet/Style/Borders.php | 29 +- .../src/PhpSpreadsheet/Style/Color.php | 51 +- .../src/PhpSpreadsheet/Style/Conditional.php | 20 +- .../src/PhpSpreadsheet/Style/Fill.php | 33 +- .../src/PhpSpreadsheet/Style/Font.php | 66 +- .../src/PhpSpreadsheet/Style/NumberFormat.php | 249 +- .../src/PhpSpreadsheet/Style/Protection.php | 19 +- .../src/PhpSpreadsheet/Style/Style.php | 56 +- .../src/PhpSpreadsheet/Style/Supervisor.php | 43 +- .../PhpSpreadsheet/Worksheet/AutoFilter.php | 74 +- .../Worksheet/AutoFilter/Column.php | 30 +- .../Worksheet/AutoFilter/Column/Rule.php | 38 +- .../PhpSpreadsheet/Worksheet/BaseDrawing.php | 37 +- .../PhpSpreadsheet/Worksheet/CellIterator.php | 12 +- .../src/PhpSpreadsheet/Worksheet/Column.php | 4 +- .../Worksheet/ColumnCellIterator.php | 61 +- .../Worksheet/ColumnDimension.php | 6 +- .../Worksheet/ColumnIterator.php | 21 +- .../PhpSpreadsheet/Worksheet/Dimension.php | 10 +- .../src/PhpSpreadsheet/Worksheet/Drawing.php | 4 +- .../Worksheet/Drawing/Shadow.php | 16 +- .../PhpSpreadsheet/Worksheet/HeaderFooter.php | 27 +- .../src/PhpSpreadsheet/Worksheet/Iterator.php | 8 +- .../Worksheet/MemoryDrawing.php | 16 +- .../PhpSpreadsheet/Worksheet/PageMargins.php | 42 +- .../PhpSpreadsheet/Worksheet/PageSetup.php | 90 +- .../PhpSpreadsheet/Worksheet/Protection.php | 145 +- .../src/PhpSpreadsheet/Worksheet/Row.php | 4 +- .../Worksheet/RowCellIterator.php | 40 +- .../PhpSpreadsheet/Worksheet/RowDimension.php | 6 +- .../PhpSpreadsheet/Worksheet/RowIterator.php | 21 +- .../PhpSpreadsheet/Worksheet/SheetView.php | 14 +- .../PhpSpreadsheet/Worksheet/Worksheet.php | 278 +- .../src/PhpSpreadsheet/Writer/BaseWriter.php | 45 + .../src/PhpSpreadsheet/Writer/Csv.php | 82 +- .../src/PhpSpreadsheet/Writer/Html.php | 1361 +-- .../src/PhpSpreadsheet/Writer/IWriter.php | 8 +- .../src/PhpSpreadsheet/Writer/Ods.php | 82 +- .../Writer/Ods/Cell/Comment.php | 5 +- .../src/PhpSpreadsheet/Writer/Ods/Content.php | 59 +- .../src/PhpSpreadsheet/Writer/Ods/Formula.php | 119 + .../src/PhpSpreadsheet/Writer/Ods/Meta.php | 4 +- .../src/PhpSpreadsheet/Writer/Ods/MetaInf.php | 2 - .../PhpSpreadsheet/Writer/Ods/Mimetype.php | 2 +- .../Writer/Ods/NamedExpressions.php | 126 + .../PhpSpreadsheet/Writer/Ods/Settings.php | 4 +- .../src/PhpSpreadsheet/Writer/Ods/Styles.php | 4 +- .../PhpSpreadsheet/Writer/Ods/Thumbnails.php | 2 +- .../PhpSpreadsheet/Writer/Ods/WriterPart.php | 2 - .../src/PhpSpreadsheet/Writer/Pdf.php | 46 +- .../src/PhpSpreadsheet/Writer/Pdf/Dompdf.php | 12 +- .../src/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 14 +- .../src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 24 +- .../src/PhpSpreadsheet/Writer/Xls.php | 901 ++ .../PhpSpreadsheet/Writer/Xls/BIFFwriter.php | 224 + .../src/PhpSpreadsheet/Writer/Xls/Escher.php | 510 ++ .../src/PhpSpreadsheet/Writer/Xls/Font.php | 147 + .../src/PhpSpreadsheet/Writer/Xls/Parser.php | 1483 +++ .../PhpSpreadsheet/Writer/Xls/Workbook.php | 1191 +++ .../PhpSpreadsheet/Writer/Xls/Worksheet.php | 4491 ++++++++++ .../src/PhpSpreadsheet/Writer/Xls/Xf.php | 548 ++ .../src/PhpSpreadsheet/Writer/Xlsx.php | 508 +- .../src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 51 +- .../PhpSpreadsheet/Writer/Xlsx/Comments.php | 14 +- .../Writer/Xlsx/ContentTypes.php | 13 +- .../Writer/Xlsx/DefinedNames.php | 223 + .../PhpSpreadsheet/Writer/Xlsx/DocProps.php | 12 - .../PhpSpreadsheet/Writer/Xlsx/Drawing.php | 22 +- .../src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 43 +- .../PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php | 4 - .../PhpSpreadsheet/Writer/Xlsx/RelsVBA.php | 4 - .../Writer/Xlsx/StringTable.php | 19 +- .../src/PhpSpreadsheet/Writer/Xlsx/Style.php | 60 +- .../src/PhpSpreadsheet/Writer/Xlsx/Theme.php | 9 - .../PhpSpreadsheet/Writer/Xlsx/Workbook.php | 219 +- .../PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 408 +- .../PhpSpreadsheet/Writer/Xlsx/WriterPart.php | 2 - .../src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php | 159 + .../vendor/psr/http-client/CHANGELOG.md | 23 + .../vendor/psr/http-client/LICENSE | 19 + .../vendor/psr/http-client/README.md | 12 + .../vendor/psr/http-client/composer.json | 27 + .../src/ClientExceptionInterface.php | 10 + .../psr/http-client/src/ClientInterface.php | 20 + .../src/NetworkExceptionInterface.php | 24 + .../src/RequestExceptionInterface.php | 24 + .../vendor/psr/http-factory/.gitignore | 2 + .../vendor/psr/http-factory/.pullapprove.yml | 7 + .../vendor/psr/http-factory/LICENSE | 21 + .../vendor/psr/http-factory/README.md | 10 + .../vendor/psr/http-factory/composer.json | 35 + .../src/RequestFactoryInterface.php | 18 + .../src/ResponseFactoryInterface.php | 18 + .../src/ServerRequestFactoryInterface.php | 24 + .../src/StreamFactoryInterface.php | 45 + .../src/UploadedFileFactoryInterface.php | 34 + .../http-factory/src/UriFactoryInterface.php | 17 + .../vendor/psr/http-message/CHANGELOG.md | 36 + .../vendor/psr/http-message/LICENSE | 19 + .../vendor/psr/http-message/README.md | 13 + .../vendor/psr/http-message/composer.json | 26 + .../psr/http-message/src/MessageInterface.php | 187 + .../psr/http-message/src/RequestInterface.php | 129 + .../http-message/src/ResponseInterface.php | 68 + .../src/ServerRequestInterface.php | 261 + .../psr/http-message/src/StreamInterface.php | 158 + .../src/UploadedFileInterface.php | 123 + .../psr/http-message/src/UriInterface.php | 323 + .../vendor/symfony/polyfill-mbstring/LICENSE | 19 + .../symfony/polyfill-mbstring/Mbstring.php | 869 ++ .../symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.php | 1397 +++ .../Resources/unidata/titleCaseRegexp.php | 5 + .../Resources/unidata/upperCase.php | 1414 +++ .../symfony/polyfill-mbstring/bootstrap.php | 147 + .../symfony/polyfill-mbstring/bootstrap80.php | 143 + .../symfony/polyfill-mbstring/composer.json | 38 + 759 files changed, 74860 insertions(+), 13105 deletions(-) create mode 100644 lib/phpspreadsheet/.DS_Store create mode 100644 lib/phpspreadsheet/vendor/composer/InstalledVersions.php create mode 100644 lib/phpspreadsheet/vendor/composer/installed.php create mode 100644 lib/phpspreadsheet/vendor/composer/platform_check.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/CREDITS create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/LICENSE create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/README.md create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/VERSION create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/composer.json create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload-legacy.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.func.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.kses.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoreferrer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Length.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php create mode 100644 lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/FUNDING.yml create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/.gitignore create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/.travis.yml create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/CHANGELOG.md create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/CONTRIBUTING.md create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/LICENSE create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/README.md create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/composer.json create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/phpunit.xml.dist create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/psalm.xml create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Bigint.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/DeflateStream.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/File.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Archive.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/File.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Method.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Version.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Stream.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/ZipStream.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/BigintTest.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/ZipStreamTest.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/bootstrap.php create mode 100644 lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php delete mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/composer.7.2.json create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/infection.json.dist create mode 100644 lib/phpspreadsheet/vendor/markbaker/matrix/phpstan.neon create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/LICENSE create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/README.md create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/SECURITY.md create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/composer.json create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/psalm.xml create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/src/Enum.php create mode 100644 lib/phpspreadsheet/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/README.md delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.lock delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/mkdocs.yml delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/phpunit.xml.dist delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/Bootstrap.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php delete mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Migrator.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php create mode 100644 lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/CHANGELOG.md create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/LICENSE create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/README.md create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/composer.json create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/src/ClientExceptionInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/src/ClientInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/src/NetworkExceptionInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-client/src/RequestExceptionInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/.gitignore create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/.pullapprove.yml create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/LICENSE create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/README.md create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/composer.json create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/RequestFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/ResponseFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/StreamFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-factory/src/UriFactoryInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/CHANGELOG.md create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/LICENSE create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/README.md create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/composer.json create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/MessageInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/RequestInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/ResponseInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/ServerRequestInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/StreamInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/UploadedFileInterface.php create mode 100644 lib/phpspreadsheet/vendor/psr/http-message/src/UriInterface.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/README.md create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap80.php create mode 100644 lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/composer.json diff --git a/lib/phpspreadsheet/.DS_Store b/lib/phpspreadsheet/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { @@ -60,7 +60,7 @@ class ClassLoader public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); diff --git a/lib/phpspreadsheet/vendor/composer/InstalledVersions.php b/lib/phpspreadsheet/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000000..fe344fe10ce --- /dev/null +++ b/lib/phpspreadsheet/vendor/composer/InstalledVersions.php @@ -0,0 +1,318 @@ + + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8', + 'name' => '__root__', + ), + 'versions' => + array ( + '__root__' => + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8', + ), + 'ezyang/htmlpurifier' => + array ( + 'pretty_version' => 'v4.13.0', + 'version' => '4.13.0.0', + 'aliases' => + array ( + ), + 'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75', + ), + 'maennchen/zipstream-php' => + array ( + 'pretty_version' => '2.1.0', + 'version' => '2.1.0.0', + 'aliases' => + array ( + ), + 'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58', + ), + 'markbaker/complex' => + array ( + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', + 'aliases' => + array ( + ), + 'reference' => '9999f1432fae467bc93c53f357105b4c31bb994c', + ), + 'markbaker/matrix' => + array ( + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', + 'aliases' => + array ( + ), + 'reference' => '9567d9c4c519fbe40de01dbd1e4469dbbb66f46a', + ), + 'myclabs/php-enum' => + array ( + 'pretty_version' => '1.7.7', + 'version' => '1.7.7.0', + 'aliases' => + array ( + ), + 'reference' => 'd178027d1e679832db9f38248fcc7200647dc2b7', + ), + 'phpoffice/phpspreadsheet' => + array ( + 'pretty_version' => '1.16.0', + 'version' => '1.16.0.0', + 'aliases' => + array ( + ), + 'reference' => '76d4323b85129d0c368149c831a07a3e258b2b50', + ), + 'psr/http-client' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', + ), + 'psr/http-factory' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', + ), + 'psr/http-message' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', + ), + 'psr/simple-cache' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', + ), + 'symfony/polyfill-mbstring' => + array ( + 'pretty_version' => 'v1.22.0', + 'version' => '1.22.0.0', + 'aliases' => + array ( + ), + 'reference' => 'f377a3dd1fde44d37b9831d68dc8dea3ffd28e13', + ), + ), +); + + + + + + + +public static function getInstalledPackages() +{ +return array_keys(self::$installed['versions']); +} + + + + + + + + + +public static function isInstalled($packageName) +{ +return isset(self::$installed['versions'][$packageName]); +} + + + + + + + + + + + + + + +public static function satisfies(VersionParser $parser, $packageName, $constraint) +{ +$constraint = $parser->parseConstraints($constraint); +$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + +return $provided->matches($constraint); +} + + + + + + + + + + +public static function getVersionRanges($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +$ranges = array(); +if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { +$ranges[] = self::$installed['versions'][$packageName]['pretty_version']; +} +if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); +} +if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); +} +if (array_key_exists('provided', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); +} + +return implode(' || ', $ranges); +} + + + + + +public static function getVersion($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['version'])) { +return null; +} + +return self::$installed['versions'][$packageName]['version']; +} + + + + + +public static function getPrettyVersion($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { +return null; +} + +return self::$installed['versions'][$packageName]['pretty_version']; +} + + + + + +public static function getReference($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['reference'])) { +return null; +} + +return self::$installed['versions'][$packageName]['reference']; +} + + + + + +public static function getRootPackage() +{ +return self::$installed['root']; +} + + + + + + + +public static function getRawData() +{ +return self::$installed; +} + + + + + + + + + + + + + + + + + + + +public static function reload($data) +{ +self::$installed = $data; +} +} diff --git a/lib/phpspreadsheet/vendor/composer/autoload_classmap.php b/lib/phpspreadsheet/vendor/composer/autoload_classmap.php index 7a91153b0d8..b26f1b13b1f 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_classmap.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_classmap.php @@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/lib/phpspreadsheet/vendor/composer/autoload_files.php b/lib/phpspreadsheet/vendor/composer/autoload_files.php index 2101c3539b0..b3d6f63746c 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_files.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_files.php @@ -6,6 +6,23 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '9d8e013a5160a09477beb8e44f8ae97b' => $vendorDir . '/markbaker/matrix/classes/src/functions/adjoint.php', + '6e78d1bdea6248d6aa117229efae50f2' => $vendorDir . '/markbaker/matrix/classes/src/functions/antidiagonal.php', + '4623d87924d94f5412fe5afbf1cef31d' => $vendorDir . '/markbaker/matrix/classes/src/functions/cofactors.php', + '901fd1f6950a637ca85f66b701a45e13' => $vendorDir . '/markbaker/matrix/classes/src/functions/determinant.php', + '83057abc0e4acc99ba80154ee5d02a49' => $vendorDir . '/markbaker/matrix/classes/src/functions/diagonal.php', + '07b7fd7a434451149b4fd477fca0ce06' => $vendorDir . '/markbaker/matrix/classes/src/functions/identity.php', + 'c8d43b340583e07ae89f2a3baef2cf89' => $vendorDir . '/markbaker/matrix/classes/src/functions/inverse.php', + '499bb10ed7a3aee2ba4c09a31a85e8d1' => $vendorDir . '/markbaker/matrix/classes/src/functions/minors.php', + '1cad2e6414d652e8b1c64e8967f6f37d' => $vendorDir . '/markbaker/matrix/classes/src/functions/trace.php', + '95a7f134ac17161d07def442b3b737e8' => $vendorDir . '/markbaker/matrix/classes/src/functions/transpose.php', + 'b3a6bc628377118d4b4b8ba08d1eb949' => $vendorDir . '/markbaker/matrix/classes/src/operations/add.php', + '5fef6d0e407f3f8887266dfa4a6c534c' => $vendorDir . '/markbaker/matrix/classes/src/operations/directsum.php', + '684ba247e1385946e3babdaa054119de' => $vendorDir . '/markbaker/matrix/classes/src/operations/subtract.php', + 'aa53dcba601214d17ad405b7c291b7e8' => $vendorDir . '/markbaker/matrix/classes/src/operations/multiply.php', + '75c79eb1b25749b05a47976f32b0d8a2' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideby.php', + '6ab8ad87a734f276a6bcd5a0fe1289be' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideinto.php', 'abede361264e2ae69ec1eee813a101af' => $vendorDir . '/markbaker/complex/classes/src/functions/abs.php', '21a5860fbef5be28db5ddfbc3cca67c4' => $vendorDir . '/markbaker/complex/classes/src/functions/acos.php', '1546e3f9d127f2a9bb2d1b6c31c26ef1' => $vendorDir . '/markbaker/complex/classes/src/functions/acosh.php', @@ -48,20 +65,5 @@ return array( '883af48563631547925fa4c3b48ead07' => $vendorDir . '/markbaker/complex/classes/src/operations/multiply.php', 'f190e3308e6ca23234a2875edc985c03' => $vendorDir . '/markbaker/complex/classes/src/operations/divideby.php', 'ac9e33ce6841aa5bf5d16d465a2f03a7' => $vendorDir . '/markbaker/complex/classes/src/operations/divideinto.php', - '9d8e013a5160a09477beb8e44f8ae97b' => $vendorDir . '/markbaker/matrix/classes/src/functions/adjoint.php', - '6e78d1bdea6248d6aa117229efae50f2' => $vendorDir . '/markbaker/matrix/classes/src/functions/antidiagonal.php', - '4623d87924d94f5412fe5afbf1cef31d' => $vendorDir . '/markbaker/matrix/classes/src/functions/cofactors.php', - '901fd1f6950a637ca85f66b701a45e13' => $vendorDir . '/markbaker/matrix/classes/src/functions/determinant.php', - '83057abc0e4acc99ba80154ee5d02a49' => $vendorDir . '/markbaker/matrix/classes/src/functions/diagonal.php', - '07b7fd7a434451149b4fd477fca0ce06' => $vendorDir . '/markbaker/matrix/classes/src/functions/identity.php', - 'c8d43b340583e07ae89f2a3baef2cf89' => $vendorDir . '/markbaker/matrix/classes/src/functions/inverse.php', - '499bb10ed7a3aee2ba4c09a31a85e8d1' => $vendorDir . '/markbaker/matrix/classes/src/functions/minors.php', - '1cad2e6414d652e8b1c64e8967f6f37d' => $vendorDir . '/markbaker/matrix/classes/src/functions/trace.php', - '95a7f134ac17161d07def442b3b737e8' => $vendorDir . '/markbaker/matrix/classes/src/functions/transpose.php', - 'b3a6bc628377118d4b4b8ba08d1eb949' => $vendorDir . '/markbaker/matrix/classes/src/operations/add.php', - '5fef6d0e407f3f8887266dfa4a6c534c' => $vendorDir . '/markbaker/matrix/classes/src/operations/directsum.php', - '684ba247e1385946e3babdaa054119de' => $vendorDir . '/markbaker/matrix/classes/src/operations/subtract.php', - 'aa53dcba601214d17ad405b7c291b7e8' => $vendorDir . '/markbaker/matrix/classes/src/operations/multiply.php', - '75c79eb1b25749b05a47976f32b0d8a2' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideby.php', - '6ab8ad87a734f276a6bcd5a0fe1289be' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideinto.php', + '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', ); diff --git a/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php b/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php index b7fc0125dbc..1ee7a397c21 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_namespaces.php @@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'), ); diff --git a/lib/phpspreadsheet/vendor/composer/autoload_psr4.php b/lib/phpspreadsheet/vendor/composer/autoload_psr4.php index 56ef810775c..def19d8baa9 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_psr4.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_psr4.php @@ -6,8 +6,13 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'), + 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'), + 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'), 'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'), 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'), ); diff --git a/lib/phpspreadsheet/vendor/composer/autoload_real.php b/lib/phpspreadsheet/vendor/composer/autoload_real.php index 8415c2f61e3..9a3431ed05d 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_real.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit4f38f1469083ef94e54b2e3d87999c8f +class ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b { private static $loader; @@ -13,21 +13,26 @@ class ComposerAutoloaderInit4f38f1469083ef94e54b2e3d87999c8f } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit4f38f1469083ef94e54b2e3d87999c8f', 'loadClassLoader'), true, true); + require __DIR__ . '/platform_check.php'; + + spl_autoload_register(array('ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit4f38f1469083ef94e54b2e3d87999c8f', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit596026141085fc6d905fff9fde42dc1b', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit596026141085fc6d905fff9fde42dc1b::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -48,19 +53,19 @@ class ComposerAutoloaderInit4f38f1469083ef94e54b2e3d87999c8f $loader->register(true); if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f::$files; + $includeFiles = Composer\Autoload\ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire4f38f1469083ef94e54b2e3d87999c8f($fileIdentifier, $file); + composerRequire596026141085fc6d905fff9fde42dc1b($fileIdentifier, $file); } return $loader; } } -function composerRequire4f38f1469083ef94e54b2e3d87999c8f($fileIdentifier, $file) +function composerRequire596026141085fc6d905fff9fde42dc1b($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; diff --git a/lib/phpspreadsheet/vendor/composer/autoload_static.php b/lib/phpspreadsheet/vendor/composer/autoload_static.php index bf6bf70d231..3d9fbd50144 100644 --- a/lib/phpspreadsheet/vendor/composer/autoload_static.php +++ b/lib/phpspreadsheet/vendor/composer/autoload_static.php @@ -4,9 +4,26 @@ namespace Composer\Autoload; -class ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f +class ComposerStaticInit596026141085fc6d905fff9fde42dc1b { public static $files = array ( + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '9d8e013a5160a09477beb8e44f8ae97b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/adjoint.php', + '6e78d1bdea6248d6aa117229efae50f2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/antidiagonal.php', + '4623d87924d94f5412fe5afbf1cef31d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/cofactors.php', + '901fd1f6950a637ca85f66b701a45e13' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/determinant.php', + '83057abc0e4acc99ba80154ee5d02a49' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/diagonal.php', + '07b7fd7a434451149b4fd477fca0ce06' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/identity.php', + 'c8d43b340583e07ae89f2a3baef2cf89' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/inverse.php', + '499bb10ed7a3aee2ba4c09a31a85e8d1' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/minors.php', + '1cad2e6414d652e8b1c64e8967f6f37d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/trace.php', + '95a7f134ac17161d07def442b3b737e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/transpose.php', + 'b3a6bc628377118d4b4b8ba08d1eb949' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/add.php', + '5fef6d0e407f3f8887266dfa4a6c534c' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/directsum.php', + '684ba247e1385946e3babdaa054119de' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/subtract.php', + 'aa53dcba601214d17ad405b7c291b7e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/multiply.php', + '75c79eb1b25749b05a47976f32b0d8a2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideby.php', + '6ab8ad87a734f276a6bcd5a0fe1289be' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideinto.php', 'abede361264e2ae69ec1eee813a101af' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/abs.php', '21a5860fbef5be28db5ddfbc3cca67c4' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acos.php', '1546e3f9d127f2a9bb2d1b6c31c26ef1' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acosh.php', @@ -49,32 +66,28 @@ class ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f '883af48563631547925fa4c3b48ead07' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/multiply.php', 'f190e3308e6ca23234a2875edc985c03' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideby.php', 'ac9e33ce6841aa5bf5d16d465a2f03a7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideinto.php', - '9d8e013a5160a09477beb8e44f8ae97b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/adjoint.php', - '6e78d1bdea6248d6aa117229efae50f2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/antidiagonal.php', - '4623d87924d94f5412fe5afbf1cef31d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/cofactors.php', - '901fd1f6950a637ca85f66b701a45e13' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/determinant.php', - '83057abc0e4acc99ba80154ee5d02a49' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/diagonal.php', - '07b7fd7a434451149b4fd477fca0ce06' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/identity.php', - 'c8d43b340583e07ae89f2a3baef2cf89' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/inverse.php', - '499bb10ed7a3aee2ba4c09a31a85e8d1' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/minors.php', - '1cad2e6414d652e8b1c64e8967f6f37d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/trace.php', - '95a7f134ac17161d07def442b3b737e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/transpose.php', - 'b3a6bc628377118d4b4b8ba08d1eb949' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/add.php', - '5fef6d0e407f3f8887266dfa4a6c534c' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/directsum.php', - '684ba247e1385946e3babdaa054119de' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/subtract.php', - 'aa53dcba601214d17ad405b7c291b7e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/multiply.php', - '75c79eb1b25749b05a47976f32b0d8a2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideby.php', - '6ab8ad87a734f276a6bcd5a0fe1289be' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideinto.php', + '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', ); public static $prefixLengthsPsr4 = array ( + 'Z' => + array ( + 'ZipStream\\' => 10, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Mbstring\\' => 26, + ), 'P' => array ( 'Psr\\SimpleCache\\' => 16, + 'Psr\\Http\\Message\\' => 17, + 'Psr\\Http\\Client\\' => 16, 'PhpOffice\\PhpSpreadsheet\\' => 25, ), 'M' => array ( + 'MyCLabs\\Enum\\' => 13, 'Matrix\\' => 7, ), 'C' => @@ -84,14 +97,35 @@ class ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f ); public static $prefixDirsPsr4 = array ( + 'ZipStream\\' => + array ( + 0 => __DIR__ . '/..' . '/maennchen/zipstream-php/src', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), 'Psr\\SimpleCache\\' => array ( 0 => __DIR__ . '/..' . '/psr/simple-cache/src', ), + 'Psr\\Http\\Message\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-message/src', + 1 => __DIR__ . '/..' . '/psr/http-factory/src', + ), + 'Psr\\Http\\Client\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/http-client/src', + ), 'PhpOffice\\PhpSpreadsheet\\' => array ( 0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet', ), + 'MyCLabs\\Enum\\' => + array ( + 0 => __DIR__ . '/..' . '/myclabs/php-enum/src', + ), 'Matrix\\' => array ( 0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src', @@ -102,11 +136,27 @@ class ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f ), ); + public static $prefixesPsr0 = array ( + 'H' => + array ( + 'HTMLPurifier' => + array ( + 0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library', + ), + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit4f38f1469083ef94e54b2e3d87999c8f::$prefixDirsPsr4; + $loader->prefixLengthsPsr4 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$prefixesPsr0; + $loader->classMap = ComposerStaticInit596026141085fc6d905fff9fde42dc1b::$classMap; }, null, ClassLoader::class); } diff --git a/lib/phpspreadsheet/vendor/composer/installed.json b/lib/phpspreadsheet/vendor/composer/installed.json index be7af015d3e..aac52851b06 100644 --- a/lib/phpspreadsheet/vendor/composer/installed.json +++ b/lib/phpspreadsheet/vendor/composer/installed.json @@ -1,315 +1,789 @@ -[ - { - "name": "markbaker/complex", - "version": "1.4.7", - "version_normalized": "1.4.7.0", - "source": { - "type": "git", - "url": "https://github.com/MarkBaker/PHPComplex.git", - "reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/1ea674a8308baf547cbcbd30c5fcd6d301b7c000", - "reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000", - "shasum": "" - }, - "require": { - "php": "^5.6.0|^7.0.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3", - "phpcompatibility/php-compatibility": "^8.0", - "phpdocumentor/phpdocumentor": "2.*", - "phploc/phploc": "2.*", - "phpmd/phpmd": "2.*", - "phpunit/phpunit": "^4.8.35|^5.4.0", - "sebastian/phpcpd": "2.*", - "squizlabs/php_codesniffer": "^3.3.0" - }, - "time": "2018-10-13T23:28:42+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Complex\\": "classes/src/" +{ + "packages": [ + { + "name": "ezyang/htmlpurifier", + "version": "v4.13.0", + "version_normalized": "4.13.0.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75" }, - "files": [ - "classes/src/functions/abs.php", - "classes/src/functions/acos.php", - "classes/src/functions/acosh.php", - "classes/src/functions/acot.php", - "classes/src/functions/acoth.php", - "classes/src/functions/acsc.php", - "classes/src/functions/acsch.php", - "classes/src/functions/argument.php", - "classes/src/functions/asec.php", - "classes/src/functions/asech.php", - "classes/src/functions/asin.php", - "classes/src/functions/asinh.php", - "classes/src/functions/atan.php", - "classes/src/functions/atanh.php", - "classes/src/functions/conjugate.php", - "classes/src/functions/cos.php", - "classes/src/functions/cosh.php", - "classes/src/functions/cot.php", - "classes/src/functions/coth.php", - "classes/src/functions/csc.php", - "classes/src/functions/csch.php", - "classes/src/functions/exp.php", - "classes/src/functions/inverse.php", - "classes/src/functions/ln.php", - "classes/src/functions/log2.php", - "classes/src/functions/log10.php", - "classes/src/functions/negative.php", - "classes/src/functions/pow.php", - "classes/src/functions/rho.php", - "classes/src/functions/sec.php", - "classes/src/functions/sech.php", - "classes/src/functions/sin.php", - "classes/src/functions/sinh.php", - "classes/src/functions/sqrt.php", - "classes/src/functions/tan.php", - "classes/src/functions/tanh.php", - "classes/src/functions/theta.php", - "classes/src/operations/add.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" - } - ], - "description": "PHP Class for working with complex numbers", - "homepage": "https://github.com/MarkBaker/PHPComplex", - "keywords": [ - "complex", - "mathematics" - ] - }, - { - "name": "markbaker/matrix", - "version": "1.2.0", - "version_normalized": "1.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/MarkBaker/PHPMatrix.git", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/5348c5a67e3b75cd209d70103f916a93b1f1ed21", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21", - "shasum": "" - }, - "require": { - "php": "^5.6.0|^7.0.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "dev-master", - "phpcompatibility/php-compatibility": "dev-master", - "phploc/phploc": "^4", - "phpmd/phpmd": "dev-master", - "phpunit/phpunit": "^5.7", - "sebastian/phpcpd": "^3.0", - "squizlabs/php_codesniffer": "^3.0@dev" - }, - "time": "2019-10-06T11:29:25+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Matrix\\": "classes/src/" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75", + "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75", + "shasum": "" }, - "files": [ - "classes/src/functions/adjoint.php", - "classes/src/functions/antidiagonal.php", - "classes/src/functions/cofactors.php", - "classes/src/functions/determinant.php", - "classes/src/functions/diagonal.php", - "classes/src/functions/identity.php", - "classes/src/functions/inverse.php", - "classes/src/functions/minors.php", - "classes/src/functions/trace.php", - "classes/src/functions/transpose.php", - "classes/src/operations/add.php", - "classes/src/operations/directsum.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" - } - ], - "description": "PHP Class for working with matrices", - "homepage": "https://github.com/MarkBaker/PHPMatrix", - "keywords": [ - "mathematics", - "matrix", - "vector" - ] - }, - { - "name": "phpoffice/phpspreadsheet", - "version": "1.10.1", - "version_normalized": "1.10.1.0", - "source": { - "type": "git", - "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "1648dc9ebef6ebe0c5a172e16cf66732918416e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/1648dc9ebef6ebe0c5a172e16cf66732918416e0", - "reference": "1648dc9ebef6ebe0c5a172e16cf66732918416e0", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-dom": "*", - "ext-fileinfo": "*", - "ext-gd": "*", - "ext-iconv": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-xml": "*", - "ext-xmlreader": "*", - "ext-xmlwriter": "*", - "ext-zip": "*", - "ext-zlib": "*", - "markbaker/complex": "^1.4", - "markbaker/matrix": "^1.2", - "php": "^7.1", - "psr/simple-cache": "^1.0" - }, - "require-dev": { - "dompdf/dompdf": "^0.8.3", - "friendsofphp/php-cs-fixer": "^2.16", - "jpgraph/jpgraph": "^4.0", - "mpdf/mpdf": "^8.0", - "phpcompatibility/php-compatibility": "^9.3", - "phpunit/phpunit": "^7.5", - "squizlabs/php_codesniffer": "^3.5", - "tecnickcom/tcpdf": "^6.3" - }, - "suggest": { - "dompdf/dompdf": "Option for rendering PDF with PDF Writer", - "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", - "mpdf/mpdf": "Option for rendering PDF with PDF Writer", - "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" - }, - "time": "2019-12-01T23:13:51+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Maarten Balliauw", - "homepage": "https://blog.maartenballiauw.be" + "require": { + "php": ">=5.2" }, - { - "name": "Mark Baker", - "homepage": "https://markbakeruk.net" + "require-dev": { + "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd" }, - { - "name": "Franck Lefevre", - "homepage": "https://rootslabs.net" + "time": "2020-06-29T00:56:53+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "HTMLPurifier": "library/" + }, + "files": [ + "library/HTMLPurifier.composer.php" + ], + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] }, - { - "name": "Erik Tilt" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/master" }, - { - "name": "Adrien Crivelli" - } - ], - "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", - "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", - "keywords": [ - "OpenXML", - "excel", - "gnumeric", - "ods", - "php", - "spreadsheet", - "xls", - "xlsx" - ] - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "version_normalized": "1.0.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "install-path": "../ezyang/htmlpurifier" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" + { + "name": "maennchen/zipstream-php", + "version": "2.1.0", + "version_normalized": "2.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58", + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58", + "shasum": "" + }, + "require": { + "myclabs/php-enum": "^1.5", + "php": ">= 7.1", + "psr/http-message": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "ext-zip": "*", + "guzzlehttp/guzzle": ">= 6.3", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": ">= 7.5" + }, + "time": "2020-05-30T13:11:16+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/master" + }, + "funding": [ + { + "url": "https://opencollective.com/zipstream", + "type": "open_collective" + } + ], + "install-path": "../maennchen/zipstream-php" }, - "require": { - "php": ">=5.3.0" + { + "name": "markbaker/complex", + "version": "2.0.0", + "version_normalized": "2.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "9999f1432fae467bc93c53f357105b4c31bb994c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/9999f1432fae467bc93c53f357105b4c31bb994c", + "reference": "9999f1432fae467bc93c53f357105b4c31bb994c", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "phpcompatibility/php-compatibility": "^9.0", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.4" + }, + "time": "2020-08-26T10:42:07+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + }, + "files": [ + "classes/src/functions/abs.php", + "classes/src/functions/acos.php", + "classes/src/functions/acosh.php", + "classes/src/functions/acot.php", + "classes/src/functions/acoth.php", + "classes/src/functions/acsc.php", + "classes/src/functions/acsch.php", + "classes/src/functions/argument.php", + "classes/src/functions/asec.php", + "classes/src/functions/asech.php", + "classes/src/functions/asin.php", + "classes/src/functions/asinh.php", + "classes/src/functions/atan.php", + "classes/src/functions/atanh.php", + "classes/src/functions/conjugate.php", + "classes/src/functions/cos.php", + "classes/src/functions/cosh.php", + "classes/src/functions/cot.php", + "classes/src/functions/coth.php", + "classes/src/functions/csc.php", + "classes/src/functions/csch.php", + "classes/src/functions/exp.php", + "classes/src/functions/inverse.php", + "classes/src/functions/ln.php", + "classes/src/functions/log2.php", + "classes/src/functions/log10.php", + "classes/src/functions/negative.php", + "classes/src/functions/pow.php", + "classes/src/functions/rho.php", + "classes/src/functions/sec.php", + "classes/src/functions/sech.php", + "classes/src/functions/sin.php", + "classes/src/functions/sinh.php", + "classes/src/functions/sqrt.php", + "classes/src/functions/tan.php", + "classes/src/functions/tanh.php", + "classes/src/functions/theta.php", + "classes/src/operations/add.php", + "classes/src/operations/subtract.php", + "classes/src/operations/multiply.php", + "classes/src/operations/divideby.php", + "classes/src/operations/divideinto.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/PHP8" + }, + "install-path": "../markbaker/complex" }, - "time": "2017-10-23T01:57:42+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + { + "name": "markbaker/matrix", + "version": "2.0.0", + "version_normalized": "2.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "9567d9c4c519fbe40de01dbd1e4469dbbb66f46a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/9567d9c4c519fbe40de01dbd1e4469dbbb66f46a", + "reference": "9567d9c4c519fbe40de01dbd1e4469dbbb66f46a", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "phpcompatibility/php-compatibility": "^9.0", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.4" + }, + "time": "2020-08-28T17:11:00+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + }, + "files": [ + "classes/src/functions/adjoint.php", + "classes/src/functions/antidiagonal.php", + "classes/src/functions/cofactors.php", + "classes/src/functions/determinant.php", + "classes/src/functions/diagonal.php", + "classes/src/functions/identity.php", + "classes/src/functions/inverse.php", + "classes/src/functions/minors.php", + "classes/src/functions/trace.php", + "classes/src/functions/transpose.php", + "classes/src/operations/add.php", + "classes/src/operations/directsum.php", + "classes/src/operations/subtract.php", + "classes/src/operations/multiply.php", + "classes/src/operations/divideby.php", + "classes/src/operations/divideinto.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/PHP8" + }, + "install-path": "../markbaker/matrix" }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } + { + "name": "myclabs/php-enum", + "version": "1.7.7", + "version_normalized": "1.7.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7", + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^3.8" + }, + "time": "2020-11-14T18:14:52+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/1.7.7" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", + "type": "tidelift" + } + ], + "install-path": "../myclabs/php-enum" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ] - } -] + { + "name": "phpoffice/phpspreadsheet", + "version": "1.16.0", + "version_normalized": "1.16.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "76d4323b85129d0c368149c831a07a3e258b2b50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/76d4323b85129d0c368149c831a07a3e258b2b50", + "reference": "76d4323b85129d0c368149c831a07a3e258b2b50", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.13", + "maennchen/zipstream-php": "^2.1", + "markbaker/complex": "^1.5||^2.0", + "markbaker/matrix": "^1.2||^2.0", + "php": "^7.2||^8.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0" + }, + "require-dev": { + "dompdf/dompdf": "^0.8.5", + "friendsofphp/php-cs-fixer": "^2.16", + "jpgraph/jpgraph": "^4.0", + "mpdf/mpdf": "^8.0", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^8.5||^9.3", + "squizlabs/php_codesniffer": "^3.5", + "tecnickcom/tcpdf": "^6.3" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", + "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)" + }, + "time": "2020-12-31T18:03:49+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.16.0" + }, + "install-path": "../phpoffice/phpspreadsheet" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "time": "2020-06-29T06:28:15+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "install-path": "../psr/http-client" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "time": "2019-04-30T12:38:16+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "install-path": "../psr/http-factory" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T14:39:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "install-path": "../psr/http-message" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2017-10-23T01:57:42+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "install-path": "../psr/simple-cache" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.0", + "version_normalized": "1.22.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2021-01-07T16:49:33+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-mbstring" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/lib/phpspreadsheet/vendor/composer/installed.php b/lib/phpspreadsheet/vendor/composer/installed.php new file mode 100644 index 00000000000..1ca76964218 --- /dev/null +++ b/lib/phpspreadsheet/vendor/composer/installed.php @@ -0,0 +1,123 @@ + + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8', + 'name' => '__root__', + ), + 'versions' => + array ( + '__root__' => + array ( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => + array ( + ), + 'reference' => '70d1b7d67bc280b21f450db41728869bd1bda8d8', + ), + 'ezyang/htmlpurifier' => + array ( + 'pretty_version' => 'v4.13.0', + 'version' => '4.13.0.0', + 'aliases' => + array ( + ), + 'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75', + ), + 'maennchen/zipstream-php' => + array ( + 'pretty_version' => '2.1.0', + 'version' => '2.1.0.0', + 'aliases' => + array ( + ), + 'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58', + ), + 'markbaker/complex' => + array ( + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', + 'aliases' => + array ( + ), + 'reference' => '9999f1432fae467bc93c53f357105b4c31bb994c', + ), + 'markbaker/matrix' => + array ( + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', + 'aliases' => + array ( + ), + 'reference' => '9567d9c4c519fbe40de01dbd1e4469dbbb66f46a', + ), + 'myclabs/php-enum' => + array ( + 'pretty_version' => '1.7.7', + 'version' => '1.7.7.0', + 'aliases' => + array ( + ), + 'reference' => 'd178027d1e679832db9f38248fcc7200647dc2b7', + ), + 'phpoffice/phpspreadsheet' => + array ( + 'pretty_version' => '1.16.0', + 'version' => '1.16.0.0', + 'aliases' => + array ( + ), + 'reference' => '76d4323b85129d0c368149c831a07a3e258b2b50', + ), + 'psr/http-client' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', + ), + 'psr/http-factory' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', + ), + 'psr/http-message' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', + ), + 'psr/simple-cache' => + array ( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'aliases' => + array ( + ), + 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', + ), + 'symfony/polyfill-mbstring' => + array ( + 'pretty_version' => 'v1.22.0', + 'version' => '1.22.0.0', + 'aliases' => + array ( + ), + 'reference' => 'f377a3dd1fde44d37b9831d68dc8dea3ffd28e13', + ), + ), +); diff --git a/lib/phpspreadsheet/vendor/composer/platform_check.php b/lib/phpspreadsheet/vendor/composer/platform_check.php new file mode 100644 index 00000000000..589e9e770b9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/CREDITS b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/CREDITS new file mode 100644 index 00000000000..7921b45af7d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/CREDITS @@ -0,0 +1,9 @@ + +CREDITS + +Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks +to the DevNetwork Community for their help (see docs/ref-devnetwork.html for +more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake +for letting me package his fantastic XSS cheatsheet for a smoketest. + + vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/LICENSE b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/LICENSE new file mode 100644 index 00000000000..8c88a20d455 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/README.md b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/README.md new file mode 100644 index 00000000000..9e0becc0ae5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/README.md @@ -0,0 +1,29 @@ +HTML Purifier [![Build Status](https://secure.travis-ci.org/ezyang/htmlpurifier.svg?branch=master)](http://travis-ci.org/ezyang/htmlpurifier) +============= + +HTML Purifier is an HTML filtering solution that uses a unique combination +of robust whitelists and aggressive parsing to ensure that not only are +XSS attacks thwarted, but the resulting HTML is standards compliant. + +HTML Purifier is oriented towards richly formatted documents from +untrusted sources that require CSS and a full tag-set. This library can +be configured to accept a more restrictive set of tags, but it won't be +as efficient as more bare-bones parsers. It will, however, do the job +right, which may be more important. + +Places to go: + +* See INSTALL for a quick installation guide +* See docs/ for developer-oriented documentation, code examples and + an in-depth installation guide. +* See WYSIWYG for information on editors like TinyMCE and FCKeditor + +HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/) + +## Installation + +Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier). + +If you're using Composer to manage dependencies, you can use + + $ composer require ezyang/htmlpurifier diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/VERSION b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/VERSION new file mode 100644 index 00000000000..01b73abe550 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/VERSION @@ -0,0 +1 @@ +4.13.0 \ No newline at end of file diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/composer.json b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/composer.json new file mode 100644 index 00000000000..0ff86b5df48 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/composer.json @@ -0,0 +1,28 @@ +{ + "name": "ezyang/htmlpurifier", + "description": "Standards compliant HTML filter written in PHP", + "type": "library", + "keywords": ["html"], + "homepage": "http://htmlpurifier.org/", + "license": "LGPL-2.1-or-later", + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "require": { + "php": ">=5.2" + }, + "require-dev": { + "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd" + }, + "autoload": { + "psr-0": { "HTMLPurifier": "library/" }, + "files": ["library/HTMLPurifier.composer.php"], + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php new file mode 100644 index 00000000000..1960c399f89 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php @@ -0,0 +1,11 @@ +purify($html, $config); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php new file mode 100644 index 00000000000..151e6752dfa --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php @@ -0,0 +1,234 @@ + $attributes) { + $allowed_elements[$element] = true; + foreach ($attributes as $attribute => $x) { + $allowed_attributes["$element.$attribute"] = true; + } + } + $config->set('HTML.AllowedElements', $allowed_elements); + $config->set('HTML.AllowedAttributes', $allowed_attributes); + if ($allowed_protocols !== null) { + $config->set('URI.AllowedSchemes', $allowed_protocols); + } + $purifier = new HTMLPurifier($config); + return $purifier->purify($string); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php new file mode 100644 index 00000000000..39b1b65319f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php @@ -0,0 +1,11 @@ +config = HTMLPurifier_Config::create($config); + $this->strategy = new HTMLPurifier_Strategy_Core(); + } + + /** + * Adds a filter to process the output. First come first serve + * + * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object + */ + public function addFilter($filter) + { + trigger_error( + 'HTMLPurifier->addFilter() is deprecated, use configuration directives' . + ' in the Filter namespace or Filter.Custom', + E_USER_WARNING + ); + $this->filters[] = $filter; + } + + /** + * Filters an HTML snippet/document to be XSS-free and standards-compliant. + * + * @param string $html String of HTML to purify + * @param HTMLPurifier_Config $config Config object for this operation, + * if omitted, defaults to the config object specified during this + * object's construction. The parameter can also be any type + * that HTMLPurifier_Config::create() supports. + * + * @return string Purified HTML + */ + public function purify($html, $config = null) + { + // :TODO: make the config merge in, instead of replace + $config = $config ? HTMLPurifier_Config::create($config) : $this->config; + + // implementation is partially environment dependant, partially + // configuration dependant + $lexer = HTMLPurifier_Lexer::create($config); + + $context = new HTMLPurifier_Context(); + + // setup HTML generator + $this->generator = new HTMLPurifier_Generator($config, $context); + $context->register('Generator', $this->generator); + + // set up global context variables + if ($config->get('Core.CollectErrors')) { + // may get moved out if other facilities use it + $language_factory = HTMLPurifier_LanguageFactory::instance(); + $language = $language_factory->create($config, $context); + $context->register('Locale', $language); + + $error_collector = new HTMLPurifier_ErrorCollector($context); + $context->register('ErrorCollector', $error_collector); + } + + // setup id_accumulator context, necessary due to the fact that + // AttrValidator can be called from many places + $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); + $context->register('IDAccumulator', $id_accumulator); + + $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context); + + // setup filters + $filter_flags = $config->getBatch('Filter'); + $custom_filters = $filter_flags['Custom']; + unset($filter_flags['Custom']); + $filters = array(); + foreach ($filter_flags as $filter => $flag) { + if (!$flag) { + continue; + } + if (strpos($filter, '.') !== false) { + continue; + } + $class = "HTMLPurifier_Filter_$filter"; + $filters[] = new $class; + } + foreach ($custom_filters as $filter) { + // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat + $filters[] = $filter; + } + $filters = array_merge($filters, $this->filters); + // maybe prepare(), but later + + for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) { + $html = $filters[$i]->preFilter($html, $config, $context); + } + + // purified HTML + $html = + $this->generator->generateFromTokens( + // list of tokens + $this->strategy->execute( + // list of un-purified tokens + $lexer->tokenizeHTML( + // un-purified HTML + $html, + $config, + $context + ), + $config, + $context + ) + ); + + for ($i = $filter_size - 1; $i >= 0; $i--) { + $html = $filters[$i]->postFilter($html, $config, $context); + } + + $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context); + $this->context =& $context; + return $html; + } + + /** + * Filters an array of HTML snippets + * + * @param string[] $array_of_html Array of html snippets + * @param HTMLPurifier_Config $config Optional config object for this operation. + * See HTMLPurifier::purify() for more details. + * + * @return string[] Array of purified HTML + */ + public function purifyArray($array_of_html, $config = null) + { + $context_array = array(); + $array = array(); + foreach($array_of_html as $key=>$value){ + if (is_array($value)) { + $array[$key] = $this->purifyArray($value, $config); + } else { + $array[$key] = $this->purify($value, $config); + } + $context_array[$key] = $this->context; + } + $this->context = $context_array; + return $array; + } + + /** + * Singleton for enforcing just one HTML Purifier in your system + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier + */ + public static function instance($prototype = null) + { + if (!self::$instance || $prototype) { + if ($prototype instanceof HTMLPurifier) { + self::$instance = $prototype; + } elseif ($prototype) { + self::$instance = new HTMLPurifier($prototype); + } else { + self::$instance = new HTMLPurifier(); + } + } + return self::$instance; + } + + /** + * Singleton for enforcing just one HTML Purifier in your system + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier + * @note Backwards compatibility, see instance() + */ + public static function getInstance($prototype = null) + { + return HTMLPurifier::instance($prototype); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php new file mode 100644 index 00000000000..a3261f8a327 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php @@ -0,0 +1,228 @@ +getHTMLDefinition(); + $parent = new HTMLPurifier_Token_Start($definition->info_parent); + $stack = array($parent->toNode()); + foreach ($tokens as $token) { + $token->skip = null; // [MUT] + $token->carryover = null; // [MUT] + if ($token instanceof HTMLPurifier_Token_End) { + $token->start = null; // [MUT] + $r = array_pop($stack); + //assert($r->name === $token->name); + //assert(empty($token->attr)); + $r->endCol = $token->col; + $r->endLine = $token->line; + $r->endArmor = $token->armor; + continue; + } + $node = $token->toNode(); + $stack[count($stack)-1]->children[] = $node; + if ($token instanceof HTMLPurifier_Token_Start) { + $stack[] = $node; + } + } + //assert(count($stack) == 1); + return $stack[0]; + } + + public static function flatten($node, $config, $context) { + $level = 0; + $nodes = array($level => new HTMLPurifier_Queue(array($node))); + $closingTokens = array(); + $tokens = array(); + do { + while (!$nodes[$level]->isEmpty()) { + $node = $nodes[$level]->shift(); // FIFO + list($start, $end) = $node->toTokenPair(); + if ($level > 0) { + $tokens[] = $start; + } + if ($end !== NULL) { + $closingTokens[$level][] = $end; + } + if ($node instanceof HTMLPurifier_Node_Element) { + $level++; + $nodes[$level] = new HTMLPurifier_Queue(); + foreach ($node->children as $childNode) { + $nodes[$level]->push($childNode); + } + } + } + $level--; + if ($level && isset($closingTokens[$level])) { + while ($token = array_pop($closingTokens[$level])) { + $tokens[] = $token; + } + } + } while ($level > 0); + return $tokens; + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php new file mode 100644 index 00000000000..c7b17cf144d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php @@ -0,0 +1,148 @@ +doConstruct($attr_types, $modules); + } + + public function doConstruct($attr_types, $modules) + { + // load extensions from the modules + foreach ($modules as $module) { + foreach ($module->attr_collections as $coll_i => $coll) { + if (!isset($this->info[$coll_i])) { + $this->info[$coll_i] = array(); + } + foreach ($coll as $attr_i => $attr) { + if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) { + // merge in includes + $this->info[$coll_i][$attr_i] = array_merge( + $this->info[$coll_i][$attr_i], + $attr + ); + continue; + } + $this->info[$coll_i][$attr_i] = $attr; + } + } + } + // perform internal expansions and inclusions + foreach ($this->info as $name => $attr) { + // merge attribute collections that include others + $this->performInclusions($this->info[$name]); + // replace string identifiers with actual attribute objects + $this->expandIdentifiers($this->info[$name], $attr_types); + } + } + + /** + * Takes a reference to an attribute associative array and performs + * all inclusions specified by the zero index. + * @param array &$attr Reference to attribute array + */ + public function performInclusions(&$attr) + { + if (!isset($attr[0])) { + return; + } + $merge = $attr[0]; + $seen = array(); // recursion guard + // loop through all the inclusions + for ($i = 0; isset($merge[$i]); $i++) { + if (isset($seen[$merge[$i]])) { + continue; + } + $seen[$merge[$i]] = true; + // foreach attribute of the inclusion, copy it over + if (!isset($this->info[$merge[$i]])) { + continue; + } + foreach ($this->info[$merge[$i]] as $key => $value) { + if (isset($attr[$key])) { + continue; + } // also catches more inclusions + $attr[$key] = $value; + } + if (isset($this->info[$merge[$i]][0])) { + // recursion + $merge = array_merge($merge, $this->info[$merge[$i]][0]); + } + } + unset($attr[0]); + } + + /** + * Expands all string identifiers in an attribute array by replacing + * them with the appropriate values inside HTMLPurifier_AttrTypes + * @param array &$attr Reference to attribute array + * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance + */ + public function expandIdentifiers(&$attr, $attr_types) + { + // because foreach will process new elements we add, make sure we + // skip duplicates + $processed = array(); + + foreach ($attr as $def_i => $def) { + // skip inclusions + if ($def_i === 0) { + continue; + } + + if (isset($processed[$def_i])) { + continue; + } + + // determine whether or not attribute is required + if ($required = (strpos($def_i, '*') !== false)) { + // rename the definition + unset($attr[$def_i]); + $def_i = trim($def_i, '*'); + $attr[$def_i] = $def; + } + + $processed[$def_i] = true; + + // if we've already got a literal object, move on + if (is_object($def)) { + // preserve previous required + $attr[$def_i]->required = ($required || $attr[$def_i]->required); + continue; + } + + if ($def === false) { + unset($attr[$def_i]); + continue; + } + + if ($t = $attr_types->get($def)) { + $attr[$def_i] = $t; + $attr[$def_i]->required = $required; + } else { + unset($attr[$def_i]); + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php new file mode 100644 index 00000000000..739646fa7c2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php @@ -0,0 +1,144 @@ + by removing + * leading and trailing whitespace, ignoring line feeds, and replacing + * carriage returns and tabs with spaces. While most useful for HTML + * attributes specified as CDATA, it can also be applied to most CSS + * values. + * + * @note This method is not entirely standards compliant, as trim() removes + * more types of whitespace than specified in the spec. In practice, + * this is rarely a problem, as those extra characters usually have + * already been removed by HTMLPurifier_Encoder. + * + * @warning This processing is inconsistent with XML's whitespace handling + * as specified by section 3.3.3 and referenced XHTML 1.0 section + * 4.7. However, note that we are NOT necessarily + * parsing XML, thus, this behavior may still be correct. We + * assume that newlines have been normalized. + */ + public function parseCDATA($string) + { + $string = trim($string); + $string = str_replace(array("\n", "\t", "\r"), ' ', $string); + return $string; + } + + /** + * Factory method for creating this class from a string. + * @param string $string String construction info + * @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string + */ + public function make($string) + { + // default implementation, return a flyweight of this object. + // If $string has an effect on the returned object (i.e. you + // need to overload this method), it is best + // to clone or instantiate new copies. (Instantiation is safer.) + return $this; + } + + /** + * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work + * properly. THIS IS A HACK! + * @param string $string a CSS colour definition + * @return string + */ + protected function mungeRgb($string) + { + $p = '\s*(\d+(\.\d+)?([%]?))\s*'; + + if (preg_match('/(rgba|hsla)\(/', $string)) { + return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string); + } + + return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string); + } + + /** + * Parses a possibly escaped CSS string and returns the "pure" + * version of it. + */ + protected function expandCSSEscape($string) + { + // flexibly parse it + $ret = ''; + for ($i = 0, $c = strlen($string); $i < $c; $i++) { + if ($string[$i] === '\\') { + $i++; + if ($i >= $c) { + $ret .= '\\'; + break; + } + if (ctype_xdigit($string[$i])) { + $code = $string[$i]; + for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { + if (!ctype_xdigit($string[$i])) { + break; + } + $code .= $string[$i]; + } + // We have to be extremely careful when adding + // new characters, to make sure we're not breaking + // the encoding. + $char = HTMLPurifier_Encoder::unichr(hexdec($code)); + if (HTMLPurifier_Encoder::cleanUTF8($char) === '') { + continue; + } + $ret .= $char; + if ($i < $c && trim($string[$i]) !== '') { + $i--; + } + continue; + } + if ($string[$i] === "\n") { + continue; + } + } + $ret .= $string[$i]; + } + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php new file mode 100644 index 00000000000..ad2cb90ad1f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php @@ -0,0 +1,136 @@ +parseCDATA($css); + + $definition = $config->getCSSDefinition(); + $allow_duplicates = $config->get("CSS.AllowDuplicates"); + + + // According to the CSS2.1 spec, the places where a + // non-delimiting semicolon can appear are in strings + // escape sequences. So here is some dumb hack to + // handle quotes. + $len = strlen($css); + $accum = ""; + $declarations = array(); + $quoted = false; + for ($i = 0; $i < $len; $i++) { + $c = strcspn($css, ";'\"", $i); + $accum .= substr($css, $i, $c); + $i += $c; + if ($i == $len) break; + $d = $css[$i]; + if ($quoted) { + $accum .= $d; + if ($d == $quoted) { + $quoted = false; + } + } else { + if ($d == ";") { + $declarations[] = $accum; + $accum = ""; + } else { + $accum .= $d; + $quoted = $d; + } + } + } + if ($accum != "") $declarations[] = $accum; + + $propvalues = array(); + $new_declarations = ''; + + /** + * Name of the current CSS property being validated. + */ + $property = false; + $context->register('CurrentCSSProperty', $property); + + foreach ($declarations as $declaration) { + if (!$declaration) { + continue; + } + if (!strpos($declaration, ':')) { + continue; + } + list($property, $value) = explode(':', $declaration, 2); + $property = trim($property); + $value = trim($value); + $ok = false; + do { + if (isset($definition->info[$property])) { + $ok = true; + break; + } + if (ctype_lower($property)) { + break; + } + $property = strtolower($property); + if (isset($definition->info[$property])) { + $ok = true; + break; + } + } while (0); + if (!$ok) { + continue; + } + // inefficient call, since the validator will do this again + if (strtolower(trim($value)) !== 'inherit') { + // inherit works for everything (but only on the base property) + $result = $definition->info[$property]->validate( + $value, + $config, + $context + ); + } else { + $result = 'inherit'; + } + if ($result === false) { + continue; + } + if ($allow_duplicates) { + $new_declarations .= "$property:$result;"; + } else { + $propvalues[$property] = $result; + } + } + + $context->destroy('CurrentCSSProperty'); + + // procedure does not write the new CSS simultaneously, so it's + // slightly inefficient, but it's the only way of getting rid of + // duplicates. Perhaps config to optimize it, but not now. + + foreach ($propvalues as $prop => $value) { + $new_declarations .= "$prop:$value;"; + } + + return $new_declarations ? $new_declarations : false; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php new file mode 100644 index 00000000000..af2b83dff83 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php @@ -0,0 +1,34 @@ + 1.0) { + $result = '1'; + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php new file mode 100644 index 00000000000..7f1ea3b0f1d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php @@ -0,0 +1,111 @@ +getCSSDefinition(); + $this->info['background-color'] = $def->info['background-color']; + $this->info['background-image'] = $def->info['background-image']; + $this->info['background-repeat'] = $def->info['background-repeat']; + $this->info['background-attachment'] = $def->info['background-attachment']; + $this->info['background-position'] = $def->info['background-position']; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') { + return false; + } + + // munge rgb() decl if necessary + $string = $this->mungeRgb($string); + + // assumes URI doesn't have spaces in it + $bits = explode(' ', $string); // bits to process + + $caught = array(); + $caught['color'] = false; + $caught['image'] = false; + $caught['repeat'] = false; + $caught['attachment'] = false; + $caught['position'] = false; + + $i = 0; // number of catches + + foreach ($bits as $bit) { + if ($bit === '') { + continue; + } + foreach ($caught as $key => $status) { + if ($key != 'position') { + if ($status !== false) { + continue; + } + $r = $this->info['background-' . $key]->validate($bit, $config, $context); + } else { + $r = $bit; + } + if ($r === false) { + continue; + } + if ($key == 'position') { + if ($caught[$key] === false) { + $caught[$key] = ''; + } + $caught[$key] .= $r . ' '; + } else { + $caught[$key] = $r; + } + $i++; + break; + } + } + + if (!$i) { + return false; + } + if ($caught['position'] !== false) { + $caught['position'] = $this->info['background-position']-> + validate($caught['position'], $config, $context); + } + + $ret = array(); + foreach ($caught as $value) { + if ($value === false) { + continue; + } + $ret[] = $value; + } + + if (empty($ret)) { + return false; + } + return implode(' ', $ret); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php new file mode 100644 index 00000000000..4580ef5a91e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php @@ -0,0 +1,157 @@ + | | left | center | right + ] + [ + | | top | center | bottom + ]? + ] | + [ // this signifies that the vertical and horizontal adjectives + // can be arbitrarily ordered, however, there can only be two, + // one of each, or none at all + [ + left | center | right + ] || + [ + top | center | bottom + ] + ] + top, left = 0% + center, (none) = 50% + bottom, right = 100% +*/ + +/* QuirksMode says: + keyword + length/percentage must be ordered correctly, as per W3C + + Internet Explorer and Opera, however, support arbitrary ordering. We + should fix it up. + + Minor issue though, not strictly necessary. +*/ + +// control freaks may appreciate the ability to convert these to +// percentages or something, but it's not necessary + +/** + * Validates the value of background-position. + */ +class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef +{ + + /** + * @type HTMLPurifier_AttrDef_CSS_Length + */ + protected $length; + + /** + * @type HTMLPurifier_AttrDef_CSS_Percentage + */ + protected $percentage; + + public function __construct() + { + $this->length = new HTMLPurifier_AttrDef_CSS_Length(); + $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = $this->parseCDATA($string); + $bits = explode(' ', $string); + + $keywords = array(); + $keywords['h'] = false; // left, right + $keywords['v'] = false; // top, bottom + $keywords['ch'] = false; // center (first word) + $keywords['cv'] = false; // center (second word) + $measures = array(); + + $i = 0; + + $lookup = array( + 'top' => 'v', + 'bottom' => 'v', + 'left' => 'h', + 'right' => 'h', + 'center' => 'c' + ); + + foreach ($bits as $bit) { + if ($bit === '') { + continue; + } + + // test for keyword + $lbit = ctype_lower($bit) ? $bit : strtolower($bit); + if (isset($lookup[$lbit])) { + $status = $lookup[$lbit]; + if ($status == 'c') { + if ($i == 0) { + $status = 'ch'; + } else { + $status = 'cv'; + } + } + $keywords[$status] = $lbit; + $i++; + } + + // test for length + $r = $this->length->validate($bit, $config, $context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + + // test for percentage + $r = $this->percentage->validate($bit, $config, $context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + } + + if (!$i) { + return false; + } // no valid values were caught + + $ret = array(); + + // first keyword + if ($keywords['h']) { + $ret[] = $keywords['h']; + } elseif ($keywords['ch']) { + $ret[] = $keywords['ch']; + $keywords['cv'] = false; // prevent re-use: center = center center + } elseif (count($measures)) { + $ret[] = array_shift($measures); + } + + if ($keywords['v']) { + $ret[] = $keywords['v']; + } elseif ($keywords['cv']) { + $ret[] = $keywords['cv']; + } elseif (count($measures)) { + $ret[] = array_shift($measures); + } + + if (empty($ret)) { + return false; + } + return implode(' ', $ret); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php new file mode 100644 index 00000000000..16243ba1ed2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php @@ -0,0 +1,56 @@ +getCSSDefinition(); + $this->info['border-width'] = $def->info['border-width']; + $this->info['border-style'] = $def->info['border-style']; + $this->info['border-top-color'] = $def->info['border-top-color']; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = $this->parseCDATA($string); + $string = $this->mungeRgb($string); + $bits = explode(' ', $string); + $done = array(); // segments we've finished + $ret = ''; // return value + foreach ($bits as $bit) { + foreach ($this->info as $propname => $validator) { + if (isset($done[$propname])) { + continue; + } + $r = $validator->validate($bit, $config, $context); + if ($r !== false) { + $ret .= $r . ' '; + $done[$propname] = true; + break; + } + } + } + return rtrim($ret); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php new file mode 100644 index 00000000000..d7287a00c2e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php @@ -0,0 +1,161 @@ +alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + } + + /** + * @param string $color + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($color, $config, $context) + { + static $colors = null; + if ($colors === null) { + $colors = $config->get('Core.ColorKeywords'); + } + + $color = trim($color); + if ($color === '') { + return false; + } + + $lower = strtolower($color); + if (isset($colors[$lower])) { + return $colors[$lower]; + } + + if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) { + $length = strlen($color); + if (strpos($color, ')') !== $length - 1) { + return false; + } + + // get used function : rgb, rgba, hsl or hsla + $function = $matches[1]; + + $parameters_size = 3; + $alpha_channel = false; + if (substr($function, -1) === 'a') { + $parameters_size = 4; + $alpha_channel = true; + } + + /* + * Allowed types for values : + * parameter_position => [type => max_value] + */ + $allowed_types = array( + 1 => array('percentage' => 100, 'integer' => 255), + 2 => array('percentage' => 100, 'integer' => 255), + 3 => array('percentage' => 100, 'integer' => 255), + ); + $allow_different_types = false; + + if (strpos($function, 'hsl') !== false) { + $allowed_types = array( + 1 => array('integer' => 360), + 2 => array('percentage' => 100), + 3 => array('percentage' => 100), + ); + $allow_different_types = true; + } + + $values = trim(str_replace($function, '', $color), ' ()'); + + $parts = explode(',', $values); + if (count($parts) !== $parameters_size) { + return false; + } + + $type = false; + $new_parts = array(); + $i = 0; + + foreach ($parts as $part) { + $i++; + $part = trim($part); + + if ($part === '') { + return false; + } + + // different check for alpha channel + if ($alpha_channel === true && $i === count($parts)) { + $result = $this->alpha->validate($part, $config, $context); + + if ($result === false) { + return false; + } + + $new_parts[] = (string)$result; + continue; + } + + if (substr($part, -1) === '%') { + $current_type = 'percentage'; + } else { + $current_type = 'integer'; + } + + if (!array_key_exists($current_type, $allowed_types[$i])) { + return false; + } + + if (!$type) { + $type = $current_type; + } + + if ($allow_different_types === false && $type != $current_type) { + return false; + } + + $max_value = $allowed_types[$i][$current_type]; + + if ($current_type == 'integer') { + // Return value between range 0 -> $max_value + $new_parts[] = (int)max(min($part, $max_value), 0); + } elseif ($current_type == 'percentage') { + $new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%'; + } + } + + $new_values = implode(',', $new_parts); + + $color = $function . '(' . $new_values . ')'; + } else { + // hexadecimal handling + if ($color[0] === '#') { + $hex = substr($color, 1); + } else { + $hex = $color; + $color = '#' . $color; + } + $length = strlen($hex); + if ($length !== 3 && $length !== 6) { + return false; + } + if (!ctype_xdigit($hex)) { + return false; + } + } + return $color; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php new file mode 100644 index 00000000000..9c1750554f5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php @@ -0,0 +1,48 @@ +defs = $defs; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + foreach ($this->defs as $i => $def) { + $result = $this->defs[$i]->validate($string, $config, $context); + if ($result !== false) { + return $result; + } + } + return false; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php new file mode 100644 index 00000000000..9d77cc9aafb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php @@ -0,0 +1,44 @@ +def = $def; + $this->element = $element; + } + + /** + * Checks if CurrentToken is set and equal to $this->element + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $token = $context->get('CurrentToken', true); + if ($token && $token->name == $this->element) { + return false; + } + return $this->def->validate($string, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php new file mode 100644 index 00000000000..bde4c3301f6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php @@ -0,0 +1,77 @@ +intValidator = new HTMLPurifier_AttrDef_Integer(); + } + + /** + * @param string $value + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($value, $config, $context) + { + $value = $this->parseCDATA($value); + if ($value === 'none') { + return $value; + } + // if we looped this we could support multiple filters + $function_length = strcspn($value, '('); + $function = trim(substr($value, 0, $function_length)); + if ($function !== 'alpha' && + $function !== 'Alpha' && + $function !== 'progid:DXImageTransform.Microsoft.Alpha' + ) { + return false; + } + $cursor = $function_length + 1; + $parameters_length = strcspn($value, ')', $cursor); + $parameters = substr($value, $cursor, $parameters_length); + $params = explode(',', $parameters); + $ret_params = array(); + $lookup = array(); + foreach ($params as $param) { + list($key, $value) = explode('=', $param); + $key = trim($key); + $value = trim($value); + if (isset($lookup[$key])) { + continue; + } + if ($key !== 'opacity') { + continue; + } + $value = $this->intValidator->validate($value, $config, $context); + if ($value === false) { + continue; + } + $int = (int)$value; + if ($int > 100) { + $value = '100'; + } + if ($int < 0) { + $value = '0'; + } + $ret_params[] = "$key=$value"; + $lookup[$key] = true; + } + $ret_parameters = implode(',', $ret_params); + $ret_function = "$function($ret_parameters)"; + return $ret_function; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php new file mode 100644 index 00000000000..579b97ef1ca --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php @@ -0,0 +1,176 @@ +getCSSDefinition(); + $this->info['font-style'] = $def->info['font-style']; + $this->info['font-variant'] = $def->info['font-variant']; + $this->info['font-weight'] = $def->info['font-weight']; + $this->info['font-size'] = $def->info['font-size']; + $this->info['line-height'] = $def->info['line-height']; + $this->info['font-family'] = $def->info['font-family']; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + static $system_fonts = array( + 'caption' => true, + 'icon' => true, + 'menu' => true, + 'message-box' => true, + 'small-caption' => true, + 'status-bar' => true + ); + + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') { + return false; + } + + // check if it's one of the keywords + $lowercase_string = strtolower($string); + if (isset($system_fonts[$lowercase_string])) { + return $lowercase_string; + } + + $bits = explode(' ', $string); // bits to process + $stage = 0; // this indicates what we're looking for + $caught = array(); // which stage 0 properties have we caught? + $stage_1 = array('font-style', 'font-variant', 'font-weight'); + $final = ''; // output + + for ($i = 0, $size = count($bits); $i < $size; $i++) { + if ($bits[$i] === '') { + continue; + } + switch ($stage) { + case 0: // attempting to catch font-style, font-variant or font-weight + foreach ($stage_1 as $validator_name) { + if (isset($caught[$validator_name])) { + continue; + } + $r = $this->info[$validator_name]->validate( + $bits[$i], + $config, + $context + ); + if ($r !== false) { + $final .= $r . ' '; + $caught[$validator_name] = true; + break; + } + } + // all three caught, continue on + if (count($caught) >= 3) { + $stage = 1; + } + if ($r !== false) { + break; + } + case 1: // attempting to catch font-size and perhaps line-height + $found_slash = false; + if (strpos($bits[$i], '/') !== false) { + list($font_size, $line_height) = + explode('/', $bits[$i]); + if ($line_height === '') { + // ooh, there's a space after the slash! + $line_height = false; + $found_slash = true; + } + } else { + $font_size = $bits[$i]; + $line_height = false; + } + $r = $this->info['font-size']->validate( + $font_size, + $config, + $context + ); + if ($r !== false) { + $final .= $r; + // attempt to catch line-height + if ($line_height === false) { + // we need to scroll forward + for ($j = $i + 1; $j < $size; $j++) { + if ($bits[$j] === '') { + continue; + } + if ($bits[$j] === '/') { + if ($found_slash) { + return false; + } else { + $found_slash = true; + continue; + } + } + $line_height = $bits[$j]; + break; + } + } else { + // slash already found + $found_slash = true; + $j = $i; + } + if ($found_slash) { + $i = $j; + $r = $this->info['line-height']->validate( + $line_height, + $config, + $context + ); + if ($r !== false) { + $final .= '/' . $r; + } + } + $final .= ' '; + $stage = 2; + break; + } + return false; + case 2: // attempting to catch font-family + $font_family = + implode(' ', array_slice($bits, $i, $size - $i)); + $r = $this->info['font-family']->validate( + $font_family, + $config, + $context + ); + if ($r !== false) { + $final .= $r . ' '; + // processing completed successfully + return rtrim($final); + } + return false; + } + } + return false; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php new file mode 100644 index 00000000000..74e24c8816e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -0,0 +1,219 @@ +mask = '_- '; + for ($c = 'a'; $c <= 'z'; $c++) { + $this->mask .= $c; + } + for ($c = 'A'; $c <= 'Z'; $c++) { + $this->mask .= $c; + } + for ($c = '0'; $c <= '9'; $c++) { + $this->mask .= $c; + } // cast-y, but should be fine + // special bytes used by UTF-8 + for ($i = 0x80; $i <= 0xFF; $i++) { + // We don't bother excluding invalid bytes in this range, + // because the our restriction of well-formed UTF-8 will + // prevent these from ever occurring. + $this->mask .= chr($i); + } + + /* + PHP's internal strcspn implementation is + O(length of string * length of mask), making it inefficient + for large masks. However, it's still faster than + preg_match 8) + for (p = s1;;) { + spanp = s2; + do { + if (*spanp == c || p == s1_end) { + return p - s1; + } + } while (spanp++ < (s2_end - 1)); + c = *++p; + } + */ + // possible optimization: invert the mask. + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + static $generic_names = array( + 'serif' => true, + 'sans-serif' => true, + 'monospace' => true, + 'fantasy' => true, + 'cursive' => true + ); + $allowed_fonts = $config->get('CSS.AllowedFonts'); + + // assume that no font names contain commas in them + $fonts = explode(',', $string); + $final = ''; + foreach ($fonts as $font) { + $font = trim($font); + if ($font === '') { + continue; + } + // match a generic name + if (isset($generic_names[$font])) { + if ($allowed_fonts === null || isset($allowed_fonts[$font])) { + $final .= $font . ', '; + } + continue; + } + // match a quoted name + if ($font[0] === '"' || $font[0] === "'") { + $length = strlen($font); + if ($length <= 2) { + continue; + } + $quote = $font[0]; + if ($font[$length - 1] !== $quote) { + continue; + } + $font = substr($font, 1, $length - 2); + } + + $font = $this->expandCSSEscape($font); + + // $font is a pure representation of the font name + + if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { + continue; + } + + if (ctype_alnum($font) && $font !== '') { + // very simple font, allow it in unharmed + $final .= $font . ', '; + continue; + } + + // bugger out on whitespace. form feed (0C) really + // shouldn't show up regardless + $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); + + // Here, there are various classes of characters which need + // to be treated differently: + // - Alphanumeric characters are essentially safe. We + // handled these above. + // - Spaces require quoting, though most parsers will do + // the right thing if there aren't any characters that + // can be misinterpreted + // - Dashes rarely occur, but they fairly unproblematic + // for parsing/rendering purposes. + // The above characters cover the majority of Western font + // names. + // - Arbitrary Unicode characters not in ASCII. Because + // most parsers give little thought to Unicode, treatment + // of these codepoints is basically uniform, even for + // punctuation-like codepoints. These characters can + // show up in non-Western pages and are supported by most + // major browsers, for example: "MS 明朝" is a + // legitimate font-name + // . See + // the CSS3 spec for more examples: + // + // You can see live samples of these on the Internet: + // + // However, most of these fonts have ASCII equivalents: + // for example, 'MS Mincho', and it's considered + // professional to use ASCII font names instead of + // Unicode font names. Thanks Takeshi Terada for + // providing this information. + // The following characters, to my knowledge, have not been + // used to name font names. + // - Single quote. While theoretically you might find a + // font name that has a single quote in its name (serving + // as an apostrophe, e.g. Dave's Scribble), I haven't + // been able to find any actual examples of this. + // Internet Explorer's cssText translation (which I + // believe is invoked by innerHTML) normalizes any + // quoting to single quotes, and fails to escape single + // quotes. (Note that this is not IE's behavior for all + // CSS properties, just some sort of special casing for + // font-family). So a single quote *cannot* be used + // safely in the font-family context if there will be an + // innerHTML/cssText translation. Note that Firefox 3.x + // does this too. + // - Double quote. In IE, these get normalized to + // single-quotes, no matter what the encoding. (Fun + // fact, in IE8, the 'content' CSS property gained + // support, where they special cased to preserve encoded + // double quotes, but still translate unadorned double + // quotes into single quotes.) So, because their + // fixpoint behavior is identical to single quotes, they + // cannot be allowed either. Firefox 3.x displays + // single-quote style behavior. + // - Backslashes are reduced by one (so \\ -> \) every + // iteration, so they cannot be used safely. This shows + // up in IE7, IE8 and FF3 + // - Semicolons, commas and backticks are handled properly. + // - The rest of the ASCII punctuation is handled properly. + // We haven't checked what browsers do to unadorned + // versions, but this is not important as long as the + // browser doesn't /remove/ surrounding quotes (as IE does + // for HTML). + // + // With these results in hand, we conclude that there are + // various levels of safety: + // - Paranoid: alphanumeric, spaces and dashes(?) + // - International: Paranoid + non-ASCII Unicode + // - Edgy: Everything except quotes, backslashes + // - NoJS: Standards compliance, e.g. sod IE. Note that + // with some judicious character escaping (since certain + // types of escaping doesn't work) this is theoretically + // OK as long as innerHTML/cssText is not called. + // We believe that international is a reasonable default + // (that we will implement now), and once we do more + // extensive research, we may feel comfortable with dropping + // it down to edgy. + + // Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of + // str(c)spn assumes that the string was already well formed + // Unicode (which of course it is). + if (strspn($font, $this->mask) !== strlen($font)) { + continue; + } + + // Historical: + // In the absence of innerHTML/cssText, these ugly + // transforms don't pose a security risk (as \\ and \" + // might--these escapes are not supported by most browsers). + // We could try to be clever and use single-quote wrapping + // when there is a double quote present, but I have choosen + // not to implement that. (NOTE: you can reduce the amount + // of escapes by one depending on what quoting style you use) + // $font = str_replace('\\', '\\5C ', $font); + // $font = str_replace('"', '\\22 ', $font); + // $font = str_replace("'", '\\27 ', $font); + + // font possibly with spaces, requires quoting + $final .= "'$font', "; + } + $final = rtrim($final, ', '); + if ($final === '') { + return false; + } + return $final; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php new file mode 100644 index 00000000000..973002c17f0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php @@ -0,0 +1,32 @@ +def = $def; + $this->allow = $allow; + } + + /** + * Intercepts and removes !important if necessary + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + // test for ! and important tokens + $string = trim($string); + $is_important = false; + // :TODO: optimization: test directly for !important and ! important + if (strlen($string) >= 9 && substr($string, -9) === 'important') { + $temp = rtrim(substr($string, 0, -9)); + // use a temp, because we might want to restore important + if (strlen($temp) >= 1 && substr($temp, -1) === '!') { + $string = rtrim(substr($temp, 0, -1)); + $is_important = true; + } + } + $string = $this->def->validate($string, $config, $context); + if ($this->allow && $is_important) { + $string .= ' !important'; + } + return $string; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php new file mode 100644 index 00000000000..f12453a04a7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php @@ -0,0 +1,77 @@ +min = $min !== null ? HTMLPurifier_Length::make($min) : null; + $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = $this->parseCDATA($string); + + // Optimizations + if ($string === '') { + return false; + } + if ($string === '0') { + return '0'; + } + if (strlen($string) === 1) { + return false; + } + + $length = HTMLPurifier_Length::make($string); + if (!$length->isValid()) { + return false; + } + + if ($this->min) { + $c = $length->compareTo($this->min); + if ($c === false) { + return false; + } + if ($c < 0) { + return false; + } + } + if ($this->max) { + $c = $length->compareTo($this->max); + if ($c === false) { + return false; + } + if ($c > 0) { + return false; + } + } + return $length->toString(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php new file mode 100644 index 00000000000..e74d42654e2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php @@ -0,0 +1,112 @@ +getCSSDefinition(); + $this->info['list-style-type'] = $def->info['list-style-type']; + $this->info['list-style-position'] = $def->info['list-style-position']; + $this->info['list-style-image'] = $def->info['list-style-image']; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') { + return false; + } + + // assumes URI doesn't have spaces in it + $bits = explode(' ', strtolower($string)); // bits to process + + $caught = array(); + $caught['type'] = false; + $caught['position'] = false; + $caught['image'] = false; + + $i = 0; // number of catches + $none = false; + + foreach ($bits as $bit) { + if ($i >= 3) { + return; + } // optimization bit + if ($bit === '') { + continue; + } + foreach ($caught as $key => $status) { + if ($status !== false) { + continue; + } + $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); + if ($r === false) { + continue; + } + if ($r === 'none') { + if ($none) { + continue; + } else { + $none = true; + } + if ($key == 'image') { + continue; + } + } + $caught[$key] = $r; + $i++; + break; + } + } + + if (!$i) { + return false; + } + + $ret = array(); + + // construct type + if ($caught['type']) { + $ret[] = $caught['type']; + } + + // construct image + if ($caught['image']) { + $ret[] = $caught['image']; + } + + // construct position + if ($caught['position']) { + $ret[] = $caught['position']; + } + + if (empty($ret)) { + return false; + } + return implode(' ', $ret); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php new file mode 100644 index 00000000000..e707f871ca2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php @@ -0,0 +1,71 @@ +single = $single; + $this->max = $max; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = $this->mungeRgb($this->parseCDATA($string)); + if ($string === '') { + return false; + } + $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n + $length = count($parts); + $final = ''; + for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) { + if (ctype_space($parts[$i])) { + continue; + } + $result = $this->single->validate($parts[$i], $config, $context); + if ($result !== false) { + $final .= $result . ' '; + $num++; + } + } + if ($final === '') { + return false; + } + return rtrim($final); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php new file mode 100644 index 00000000000..ef49d20fdd1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php @@ -0,0 +1,90 @@ +non_negative = $non_negative; + } + + /** + * @param string $number + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string|bool + * @warning Some contexts do not pass $config, $context. These + * variables should not be used without checking HTMLPurifier_Length + */ + public function validate($number, $config, $context) + { + $number = $this->parseCDATA($number); + + if ($number === '') { + return false; + } + if ($number === '0') { + return '0'; + } + + $sign = ''; + switch ($number[0]) { + case '-': + if ($this->non_negative) { + return false; + } + $sign = '-'; + case '+': + $number = substr($number, 1); + } + + if (ctype_digit($number)) { + $number = ltrim($number, '0'); + return $number ? $sign . $number : '0'; + } + + // Period is the only non-numeric character allowed + if (strpos($number, '.') === false) { + return false; + } + + list($left, $right) = explode('.', $number, 2); + + if ($left === '' && $right === '') { + return false; + } + if ($left !== '' && !ctype_digit($left)) { + return false; + } + + // Remove leading zeros until positive number or a zero stays left + if (ltrim($left, '0') != '') { + $left = ltrim($left, '0'); + } else { + $left = '0'; + } + + $right = rtrim($right, '0'); + + if ($right === '') { + return $left ? $sign . $left : '0'; + } elseif (!ctype_digit($right)) { + return false; + } + return $sign . $left . '.' . $right; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php new file mode 100644 index 00000000000..f0f25c50a87 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php @@ -0,0 +1,54 @@ +number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative); + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = $this->parseCDATA($string); + + if ($string === '') { + return false; + } + $length = strlen($string); + if ($length === 1) { + return false; + } + if ($string[$length - 1] !== '%') { + return false; + } + + $number = substr($string, 0, $length - 1); + $number = $this->number_def->validate($number, $config, $context); + + if ($number === false) { + return false; + } + return "$number%"; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php new file mode 100644 index 00000000000..5fd4b7f7b41 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php @@ -0,0 +1,46 @@ + true, + 'overline' => true, + 'underline' => true, + ); + + $string = strtolower($this->parseCDATA($string)); + + if ($string === 'none') { + return $string; + } + + $parts = explode(' ', $string); + $final = ''; + foreach ($parts as $part) { + if (isset($allowed_values[$part])) { + $final .= $part . ' '; + } + } + $final = rtrim($final); + if ($final === '') { + return false; + } + return $final; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php new file mode 100644 index 00000000000..6617acace59 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php @@ -0,0 +1,77 @@ +parseCDATA($uri_string); + if (strpos($uri_string, 'url(') !== 0) { + return false; + } + $uri_string = substr($uri_string, 4); + if (strlen($uri_string) == 0) { + return false; + } + $new_length = strlen($uri_string) - 1; + if ($uri_string[$new_length] != ')') { + return false; + } + $uri = trim(substr($uri_string, 0, $new_length)); + + if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { + $quote = $uri[0]; + $new_length = strlen($uri) - 1; + if ($uri[$new_length] !== $quote) { + return false; + } + $uri = substr($uri, 1, $new_length - 1); + } + + $uri = $this->expandCSSEscape($uri); + + $result = parent::validate($uri, $config, $context); + + if ($result === false) { + return false; + } + + // extra sanity check; should have been done by URI + $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); + + // suspicious characters are ()'; we're going to percent encode + // them for safety. + $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result); + + // there's an extra bug where ampersands lose their escaping on + // an innerHTML cycle, so a very unlucky query parameter could + // then change the meaning of the URL. Unfortunately, there's + // not much we can do about that... + return "url(\"$result\")"; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php new file mode 100644 index 00000000000..6698a00c019 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php @@ -0,0 +1,44 @@ +clone = $clone; + } + + /** + * @param string $v + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($v, $config, $context) + { + return $this->clone->validate($v, $config, $context); + } + + /** + * @param string $string + * @return HTMLPurifier_AttrDef + */ + public function make($string) + { + return clone $this->clone; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php new file mode 100644 index 00000000000..8abda7f6e2a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php @@ -0,0 +1,73 @@ +valid_values = array_flip($valid_values); + $this->case_sensitive = $case_sensitive; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = trim($string); + if (!$this->case_sensitive) { + // we may want to do full case-insensitive libraries + $string = ctype_lower($string) ? $string : strtolower($string); + } + $result = isset($this->valid_values[$string]); + + return $result ? $string : false; + } + + /** + * @param string $string In form of comma-delimited list of case-insensitive + * valid values. Example: "foo,bar,baz". Prepend "s:" to make + * case sensitive + * @return HTMLPurifier_AttrDef_Enum + */ + public function make($string) + { + if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') { + $string = substr($string, 2); + $sensitive = true; + } else { + $sensitive = false; + } + $values = explode(',', $string); + return new HTMLPurifier_AttrDef_Enum($values, $sensitive); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php new file mode 100644 index 00000000000..be3bbc8dc07 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php @@ -0,0 +1,48 @@ +name = $name; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + return $this->name; + } + + /** + * @param string $string Name of attribute + * @return HTMLPurifier_AttrDef_HTML_Bool + */ + public function make($string) + { + return new HTMLPurifier_AttrDef_HTML_Bool($string); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php new file mode 100644 index 00000000000..d5013488fc5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php @@ -0,0 +1,48 @@ +getDefinition('HTML')->doctype->name; + if ($name == "XHTML 1.1" || $name == "XHTML 2.0") { + return parent::split($string, $config, $context); + } else { + return preg_split('/\s+/', $string); + } + } + + /** + * @param array $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + protected function filter($tokens, $config, $context) + { + $allowed = $config->get('Attr.AllowedClasses'); + $forbidden = $config->get('Attr.ForbiddenClasses'); + $ret = array(); + foreach ($tokens as $token) { + if (($allowed === null || isset($allowed[$token])) && + !isset($forbidden[$token]) && + // We need this O(n) check because of PHP's array + // implementation that casts -0 to 0. + !in_array($token, $ret, true) + ) { + $ret[] = $token; + } + } + return $ret; + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php new file mode 100644 index 00000000000..946ebb78201 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php @@ -0,0 +1,51 @@ +get('Core.ColorKeywords'); + } + + $string = trim($string); + + if (empty($string)) { + return false; + } + $lower = strtolower($string); + if (isset($colors[$lower])) { + return $colors[$lower]; + } + if ($string[0] === '#') { + $hex = substr($string, 1); + } else { + $hex = $string; + } + + $length = strlen($hex); + if ($length !== 3 && $length !== 6) { + return false; + } + if (!ctype_xdigit($hex)) { + return false; + } + if ($length === 3) { + $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; + } + return "#$hex"; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php new file mode 100644 index 00000000000..d79ba12b3f8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php @@ -0,0 +1,38 @@ +valid_values === false) { + $this->valid_values = $config->get('Attr.AllowedFrameTargets'); + } + return parent::validate($string, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php new file mode 100644 index 00000000000..4ba45610feb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php @@ -0,0 +1,113 @@ +selector = $selector; + } + + /** + * @param string $id + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($id, $config, $context) + { + if (!$this->selector && !$config->get('Attr.EnableID')) { + return false; + } + + $id = trim($id); // trim it first + + if ($id === '') { + return false; + } + + $prefix = $config->get('Attr.IDPrefix'); + if ($prefix !== '') { + $prefix .= $config->get('Attr.IDPrefixLocal'); + // prevent re-appending the prefix + if (strpos($id, $prefix) !== 0) { + $id = $prefix . $id; + } + } elseif ($config->get('Attr.IDPrefixLocal') !== '') { + trigger_error( + '%Attr.IDPrefixLocal cannot be used unless ' . + '%Attr.IDPrefix is set', + E_USER_WARNING + ); + } + + if (!$this->selector) { + $id_accumulator =& $context->get('IDAccumulator'); + if (isset($id_accumulator->ids[$id])) { + return false; + } + } + + // we purposely avoid using regex, hopefully this is faster + + if ($config->get('Attr.ID.HTML5') === true) { + if (preg_match('/[\t\n\x0b\x0c ]/', $id)) { + return false; + } + } else { + if (ctype_alpha($id)) { + // OK + } else { + if (!ctype_alpha(@$id[0])) { + return false; + } + // primitive style of regexps, I suppose + $trim = trim( + $id, + 'A..Za..z0..9:-._' + ); + if ($trim !== '') { + return false; + } + } + } + + $regexp = $config->get('Attr.IDBlacklistRegexp'); + if ($regexp && preg_match($regexp, $id)) { + return false; + } + + if (!$this->selector) { + $id_accumulator->add($id); + } + + // if no change was made to the ID, return the result + // else, return the new id if stripping whitespace made it + // valid, or return false. + return $id; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php new file mode 100644 index 00000000000..1c4006fbbda --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php @@ -0,0 +1,56 @@ + 100) { + return '100%'; + } + return ((string)$points) . '%'; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php new file mode 100644 index 00000000000..63fa04c15c0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php @@ -0,0 +1,72 @@ + 'AllowedRel', + 'rev' => 'AllowedRev' + ); + if (!isset($configLookup[$name])) { + trigger_error( + 'Unrecognized attribute name for link ' . + 'relationship.', + E_USER_ERROR + ); + return; + } + $this->name = $configLookup[$name]; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $allowed = $config->get('Attr.' . $this->name); + if (empty($allowed)) { + return false; + } + + $string = $this->parseCDATA($string); + $parts = explode(' ', $string); + + // lookup to prevent duplicates + $ret_lookup = array(); + foreach ($parts as $part) { + $part = strtolower(trim($part)); + if (!isset($allowed[$part])) { + continue; + } + $ret_lookup[$part] = true; + } + + if (empty($ret_lookup)) { + return false; + } + $string = implode(' ', array_keys($ret_lookup)); + return $string; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php new file mode 100644 index 00000000000..bbb20f2f802 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php @@ -0,0 +1,60 @@ +split($string, $config, $context); + $tokens = $this->filter($tokens, $config, $context); + if (empty($tokens)) { + return false; + } + return implode(' ', $tokens); + } + + /** + * Splits a space separated list of tokens into its constituent parts. + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + protected function split($string, $config, $context) + { + // OPTIMIZABLE! + // do the preg_match, capture all subpatterns for reformulation + + // we don't support U+00A1 and up codepoints or + // escaping because I don't know how to do that with regexps + // and plus it would complicate optimization efforts (you never + // see that anyway). + $pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start + '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' . + '(?:(?=\s)|\z)/'; // look ahead for space or string end + preg_match_all($pattern, $string, $matches); + return $matches[1]; + } + + /** + * Template method for removing certain tokens based on arbitrary criteria. + * @note If we wanted to be really functional, we'd do an array_filter + * with a callback. But... we're not. + * @param array $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + protected function filter($tokens, $config, $context) + { + return $tokens; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php new file mode 100644 index 00000000000..a1d019e095b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php @@ -0,0 +1,76 @@ +max = $max; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = trim($string); + if ($string === '0') { + return $string; + } + if ($string === '') { + return false; + } + $length = strlen($string); + if (substr($string, $length - 2) == 'px') { + $string = substr($string, 0, $length - 2); + } + if (!is_numeric($string)) { + return false; + } + $int = (int)$string; + + if ($int < 0) { + return '0'; + } + + // upper-bound value, extremely high values can + // crash operating systems, see + // WARNING, above link WILL crash you if you're using Windows + + if ($this->max !== null && $int > $this->max) { + return (string)$this->max; + } + return (string)$int; + } + + /** + * @param string $string + * @return HTMLPurifier_AttrDef + */ + public function make($string) + { + if ($string === '') { + $max = null; + } else { + $max = (int)$string; + } + $class = get_class($this); + return new $class($max); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php new file mode 100644 index 00000000000..400e707d2f5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php @@ -0,0 +1,91 @@ +negative = $negative; + $this->zero = $zero; + $this->positive = $positive; + } + + /** + * @param string $integer + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($integer, $config, $context) + { + $integer = $this->parseCDATA($integer); + if ($integer === '') { + return false; + } + + // we could possibly simply typecast it to integer, but there are + // certain fringe cases that must not return an integer. + + // clip leading sign + if ($this->negative && $integer[0] === '-') { + $digits = substr($integer, 1); + if ($digits === '0') { + $integer = '0'; + } // rm minus sign for zero + } elseif ($this->positive && $integer[0] === '+') { + $digits = $integer = substr($integer, 1); // rm unnecessary plus + } else { + $digits = $integer; + } + + // test if it's numeric + if (!ctype_digit($digits)) { + return false; + } + + // perform scope tests + if (!$this->zero && $integer == 0) { + return false; + } + if (!$this->positive && $integer > 0) { + return false; + } + if (!$this->negative && $integer < 0) { + return false; + } + + return $integer; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php new file mode 100644 index 00000000000..2a55cea642e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php @@ -0,0 +1,86 @@ + 8 || !ctype_alnum($subtags[1])) { + return $new_string; + } + if (!ctype_lower($subtags[1])) { + $subtags[1] = strtolower($subtags[1]); + } + + $new_string .= '-' . $subtags[1]; + if ($num_subtags == 2) { + return $new_string; + } + + // process all other subtags, index 2 and up + for ($i = 2; $i < $num_subtags; $i++) { + $length = strlen($subtags[$i]); + if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) { + return $new_string; + } + if (!ctype_lower($subtags[$i])) { + $subtags[$i] = strtolower($subtags[$i]); + } + $new_string .= '-' . $subtags[$i]; + } + return $new_string; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php new file mode 100644 index 00000000000..c7eb3199a4a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php @@ -0,0 +1,53 @@ +tag = $tag; + $this->withTag = $with_tag; + $this->withoutTag = $without_tag; + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $token = $context->get('CurrentToken', true); + if (!$token || $token->name !== $this->tag) { + return $this->withoutTag->validate($string, $config, $context); + } else { + return $this->withTag->validate($string, $config, $context); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php new file mode 100644 index 00000000000..4553a4ea9b6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php @@ -0,0 +1,21 @@ +parseCDATA($string); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php new file mode 100644 index 00000000000..c1cd89772c2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php @@ -0,0 +1,111 @@ +parser = new HTMLPurifier_URIParser(); + $this->embedsResource = (bool)$embeds_resource; + } + + /** + * @param string $string + * @return HTMLPurifier_AttrDef_URI + */ + public function make($string) + { + $embeds = ($string === 'embedded'); + return new HTMLPurifier_AttrDef_URI($embeds); + } + + /** + * @param string $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($uri, $config, $context) + { + if ($config->get('URI.Disable')) { + return false; + } + + $uri = $this->parseCDATA($uri); + + // parse the URI + $uri = $this->parser->parse($uri); + if ($uri === false) { + return false; + } + + // add embedded flag to context for validators + $context->register('EmbeddedURI', $this->embedsResource); + + $ok = false; + do { + + // generic validation + $result = $uri->validate($config, $context); + if (!$result) { + break; + } + + // chained filtering + $uri_def = $config->getDefinition('URI'); + $result = $uri_def->filter($uri, $config, $context); + if (!$result) { + break; + } + + // scheme-specific validation + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) { + break; + } + if ($this->embedsResource && !$scheme_obj->browsable) { + break; + } + $result = $scheme_obj->validate($uri, $config, $context); + if (!$result) { + break; + } + + // Post chained filtering + $result = $uri_def->postFilter($uri, $config, $context); + if (!$result) { + break; + } + + // survived gauntlet + $ok = true; + + } while (false); + + $context->destroy('EmbeddedURI'); + if (!$ok) { + return false; + } + // back to string + return $uri->toString(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php new file mode 100644 index 00000000000..daf32b7643b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php @@ -0,0 +1,20 @@ +" + // that needs more percent encoding to be done + if ($string == '') { + return false; + } + $string = trim($string); + $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string); + return $result ? $string : false; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php new file mode 100644 index 00000000000..1beeaa5d22e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php @@ -0,0 +1,142 @@ +ipv4 = new HTMLPurifier_AttrDef_URI_IPv4(); + $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6(); + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $length = strlen($string); + // empty hostname is OK; it's usually semantically equivalent: + // the default host as defined by a URI scheme is used: + // + // If the URI scheme defines a default for host, then that + // default applies when the host subcomponent is undefined + // or when the registered name is empty (zero length). + if ($string === '') { + return ''; + } + if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') { + //IPv6 + $ip = substr($string, 1, $length - 2); + $valid = $this->ipv6->validate($ip, $config, $context); + if ($valid === false) { + return false; + } + return '[' . $valid . ']'; + } + + // need to do checks on unusual encodings too + $ipv4 = $this->ipv4->validate($string, $config, $context); + if ($ipv4 !== false) { + return $ipv4; + } + + // A regular domain name. + + // This doesn't match I18N domain names, but we don't have proper IRI support, + // so force users to insert Punycode. + + // There is not a good sense in which underscores should be + // allowed, since it's technically not! (And if you go as + // far to allow everything as specified by the DNS spec... + // well, that's literally everything, modulo some space limits + // for the components and the overall name (which, by the way, + // we are NOT checking!). So we (arbitrarily) decide this: + // let's allow underscores wherever we would have allowed + // hyphens, if they are enabled. This is a pretty good match + // for browser behavior, for example, a large number of browsers + // cannot handle foo_.example.com, but foo_bar.example.com is + // fairly well supported. + $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : ''; + + // Based off of RFC 1738, but amended so that + // as per RFC 3696, the top label need only not be all numeric. + // The productions describing this are: + $a = '[a-z]'; // alpha + $an = '[a-z0-9]'; // alphanum + $and = "[a-z0-9-$underscore]"; // alphanum | "-" + // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + $domainlabel = "$an(?:$and*$an)?"; + // AMENDED as per RFC 3696 + // toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum + // side condition: not all numeric + $toplabel = "$an(?:$and*$an)?"; + // hostname = *( domainlabel "." ) toplabel [ "." ] + if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) { + if (!ctype_digit($matches[1])) { + return $string; + } + } + + // PHP 5.3 and later support this functionality natively + if (function_exists('idn_to_ascii')) { + if (defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46')) { + $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); + } else { + $string = idn_to_ascii($string); + } + + // If we have Net_IDNA2 support, we can support IRIs by + // punycoding them. (This is the most portable thing to do, + // since otherwise we have to assume browsers support + } elseif ($config->get('Core.EnableIDNA')) { + $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true)); + // we need to encode each period separately + $parts = explode('.', $string); + try { + $new_parts = array(); + foreach ($parts as $part) { + $encodable = false; + for ($i = 0, $c = strlen($part); $i < $c; $i++) { + if (ord($part[$i]) > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + $new_parts[] = $part; + } else { + $new_parts[] = $idna->encode($part); + } + } + $string = implode('.', $new_parts); + } catch (Exception $e) { + // XXX error reporting + } + } + // Try again + if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) { + return $string; + } + return false; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php new file mode 100644 index 00000000000..30ac16c9e73 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php @@ -0,0 +1,45 @@ +ip4) { + $this->_loadRegex(); + } + + if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) { + return $aIP; + } + return false; + } + + /** + * Lazy load function to prevent regex from being stuffed in + * cache. + */ + protected function _loadRegex() + { + $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255 + $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})"; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php new file mode 100644 index 00000000000..f243793eeb8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php @@ -0,0 +1,89 @@ +ip4) { + $this->_loadRegex(); + } + + $original = $aIP; + + $hex = '[0-9a-fA-F]'; + $blk = '(?:' . $hex . '{1,4})'; + $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128 + + // prefix check + if (strpos($aIP, '/') !== false) { + if (preg_match('#' . $pre . '$#s', $aIP, $find)) { + $aIP = substr($aIP, 0, 0 - strlen($find[0])); + unset($find); + } else { + return false; + } + } + + // IPv4-compatiblity check + if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) { + $aIP = substr($aIP, 0, 0 - strlen($find[0])); + $ip = explode('.', $find[0]); + $ip = array_map('dechex', $ip); + $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3]; + unset($find, $ip); + } + + // compression check + $aIP = explode('::', $aIP); + $c = count($aIP); + if ($c > 2) { + return false; + } elseif ($c == 2) { + list($first, $second) = $aIP; + $first = explode(':', $first); + $second = explode(':', $second); + + if (count($first) + count($second) > 8) { + return false; + } + + while (count($first) < 8) { + array_push($first, '0'); + } + + array_splice($first, 8 - count($second), 8, $second); + $aIP = $first; + unset($first, $second); + } else { + $aIP = explode(':', $aIP[0]); + } + $c = count($aIP); + + if ($c != 8) { + return false; + } + + // All the pieces should be 16-bit hex strings. Are they? + foreach ($aIP as $piece) { + if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) { + return false; + } + } + return $original; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php new file mode 100644 index 00000000000..b428331f154 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php @@ -0,0 +1,60 @@ +confiscateAttr($attr, 'background'); + // some validation should happen here + + $this->prependCSS($attr, "background-image:url($background);"); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php new file mode 100644 index 00000000000..d66c04a5b89 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php @@ -0,0 +1,27 @@ +get('Attr.DefaultTextDir'); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php new file mode 100644 index 00000000000..0f51fd2cecd --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php @@ -0,0 +1,28 @@ +confiscateAttr($attr, 'bgcolor'); + // some validation should happen here + + $this->prependCSS($attr, "background-color:$bgcolor;"); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php new file mode 100644 index 00000000000..f25cd01955f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php @@ -0,0 +1,47 @@ +attr = $attr; + $this->css = $css; + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr[$this->attr])) { + return $attr; + } + unset($attr[$this->attr]); + $this->prependCSS($attr, $this->css); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php new file mode 100644 index 00000000000..057dc017fad --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php @@ -0,0 +1,26 @@ +confiscateAttr($attr, 'border'); + // some validation should happen here + $this->prependCSS($attr, "border:{$border_width}px solid;"); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php new file mode 100644 index 00000000000..7ccd0e3fb7c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php @@ -0,0 +1,68 @@ +attr = $attr; + $this->enumToCSS = $enum_to_css; + $this->caseSensitive = (bool)$case_sensitive; + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr[$this->attr])) { + return $attr; + } + + $value = trim($attr[$this->attr]); + unset($attr[$this->attr]); + + if (!$this->caseSensitive) { + $value = strtolower($value); + } + + if (!isset($this->enumToCSS[$value])) { + return $attr; + } + $this->prependCSS($attr, $this->enumToCSS[$value]); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php new file mode 100644 index 00000000000..235ebb34b60 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php @@ -0,0 +1,47 @@ +get('Core.RemoveInvalidImg')) { + return $attr; + } + $attr['src'] = $config->get('Attr.DefaultInvalidImage'); + $src = false; + } + + if (!isset($attr['alt'])) { + if ($src) { + $alt = $config->get('Attr.DefaultImageAlt'); + if ($alt === null) { + $attr['alt'] = basename($attr['src']); + } else { + $attr['alt'] = $alt; + } + } else { + $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt'); + } + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php new file mode 100644 index 00000000000..350b3358fcb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php @@ -0,0 +1,61 @@ + array('left', 'right'), + 'vspace' => array('top', 'bottom') + ); + + /** + * @param string $attr + */ + public function __construct($attr) + { + $this->attr = $attr; + if (!isset($this->css[$attr])) { + trigger_error(htmlspecialchars($attr) . ' is not valid space attribute'); + } + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr[$this->attr])) { + return $attr; + } + + $width = $this->confiscateAttr($attr, $this->attr); + // some validation could happen here + + if (!isset($this->css[$this->attr])) { + return $attr; + } + + $style = ''; + foreach ($this->css[$this->attr] as $suffix) { + $property = "margin-$suffix"; + $style .= "$property:{$width}px;"; + } + $this->prependCSS($attr, $style); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php new file mode 100644 index 00000000000..3ab47ed8c98 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php @@ -0,0 +1,56 @@ +pixels = new HTMLPurifier_AttrDef_HTML_Pixels(); + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr['type'])) { + $t = 'text'; + } else { + $t = strtolower($attr['type']); + } + if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') { + unset($attr['checked']); + } + if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') { + unset($attr['maxlength']); + } + if (isset($attr['size']) && $t !== 'text' && $t !== 'password') { + $result = $this->pixels->validate($attr['size'], $config, $context); + if ($result === false) { + unset($attr['size']); + } else { + $attr['size'] = $result; + } + } + if (isset($attr['src']) && $t !== 'image') { + unset($attr['src']); + } + if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) { + $attr['value'] = ''; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php new file mode 100644 index 00000000000..5b0aff0e409 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php @@ -0,0 +1,31 @@ +name = $name; + $this->cssName = $css_name ? $css_name : $name; + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr[$this->name])) { + return $attr; + } + $length = $this->confiscateAttr($attr, $this->name); + if (ctype_digit($length)) { + $length .= 'px'; + } + $this->prependCSS($attr, $this->cssName . ":$length;"); + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php new file mode 100644 index 00000000000..63cce6837a5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php @@ -0,0 +1,33 @@ +get('HTML.Attr.Name.UseCDATA')) { + return $attr; + } + if (!isset($attr['name'])) { + return $attr; + } + $id = $this->confiscateAttr($attr, 'name'); + if (isset($attr['id'])) { + return $attr; + } + $attr['id'] = $id; + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php new file mode 100644 index 00000000000..36079b786ff --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php @@ -0,0 +1,41 @@ +idDef = new HTMLPurifier_AttrDef_HTML_ID(); + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr['name'])) { + return $attr; + } + $name = $attr['name']; + if (isset($attr['id']) && $attr['id'] === $name) { + return $attr; + } + $result = $this->idDef->validate($name, $config, $context); + if ($result === false) { + unset($attr['name']); + } else { + $attr['name'] = $result; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php new file mode 100644 index 00000000000..1057ebee1b1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php @@ -0,0 +1,52 @@ +parser = new HTMLPurifier_URIParser(); + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr['href'])) { + return $attr; + } + + // XXX Kind of inefficient + $url = $this->parser->parse($attr['href']); + $scheme = $url->getSchemeObj($config, $context); + + if ($scheme->browsable && !$url->isLocal($config, $context)) { + if (isset($attr['rel'])) { + $rels = explode(' ', $attr['rel']); + if (!in_array('nofollow', $rels)) { + $rels[] = 'nofollow'; + } + $attr['rel'] = implode(' ', $rels); + } else { + $attr['rel'] = 'nofollow'; + } + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php new file mode 100644 index 00000000000..231c81a3f0a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php @@ -0,0 +1,25 @@ +uri = new HTMLPurifier_AttrDef_URI(true); // embedded + $this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent')); + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + // If we add support for other objects, we'll need to alter the + // transforms. + switch ($attr['name']) { + // application/x-shockwave-flash + // Keep this synchronized with Injector/SafeObject.php + case 'allowScriptAccess': + $attr['value'] = 'never'; + break; + case 'allowNetworking': + $attr['value'] = 'internal'; + break; + case 'allowFullScreen': + if ($config->get('HTML.FlashAllowFullScreen')) { + $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false'; + } else { + $attr['value'] = 'false'; + } + break; + case 'wmode': + $attr['value'] = $this->wmode->validate($attr['value'], $config, $context); + break; + case 'movie': + case 'src': + $attr['name'] = "movie"; + $attr['value'] = $this->uri->validate($attr['value'], $config, $context); + break; + case 'flashvars': + // we're going to allow arbitrary inputs to the SWF, on + // the reasoning that it could only hack the SWF, not us. + break; + // add other cases to support other param name/value pairs + default: + $attr['name'] = $attr['value'] = null; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php new file mode 100644 index 00000000000..b7057bbf8ed --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php @@ -0,0 +1,23 @@ + + */ +class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform +{ + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr['type'])) { + $attr['type'] = 'text/javascript'; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php new file mode 100644 index 00000000000..dd63ea89cb6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php @@ -0,0 +1,45 @@ +parser = new HTMLPurifier_URIParser(); + } + + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + if (!isset($attr['href'])) { + return $attr; + } + + // XXX Kind of inefficient + $url = $this->parser->parse($attr['href']); + $scheme = $url->getSchemeObj($config, $context); + + if ($scheme->browsable && !$url->isBenign($config, $context)) { + $attr['target'] = '_blank'; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php new file mode 100644 index 00000000000..1db3c6c09ed --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php @@ -0,0 +1,37 @@ + + */ +class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform +{ + /** + * @param array $attr + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function transform($attr, $config, $context) + { + // Calculated from Firefox + if (!isset($attr['cols'])) { + $attr['cols'] = '22'; + } + if (!isset($attr['rows'])) { + $attr['rows'] = '3'; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php new file mode 100644 index 00000000000..3b70520b6ad --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php @@ -0,0 +1,96 @@ +info['Enum'] = new HTMLPurifier_AttrDef_Enum(); + $this->info['Bool'] = new HTMLPurifier_AttrDef_HTML_Bool(); + + $this->info['CDATA'] = new HTMLPurifier_AttrDef_Text(); + $this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID(); + $this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length(); + $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength(); + $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens(); + $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels(); + $this->info['Text'] = new HTMLPurifier_AttrDef_Text(); + $this->info['URI'] = new HTMLPurifier_AttrDef_URI(); + $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang(); + $this->info['Color'] = new HTMLPurifier_AttrDef_HTML_Color(); + $this->info['IAlign'] = self::makeEnum('top,middle,bottom,left,right'); + $this->info['LAlign'] = self::makeEnum('top,bottom,left,right'); + $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget(); + + // unimplemented aliases + $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text(); + $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text(); + $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text(); + $this->info['Character'] = new HTMLPurifier_AttrDef_Text(); + + // "proprietary" types + $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class(); + + // number is really a positive integer (one or more digits) + // FIXME: ^^ not always, see start and value of list items + $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); + } + + private static function makeEnum($in) + { + return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in))); + } + + /** + * Retrieves a type + * @param string $type String type name + * @return HTMLPurifier_AttrDef Object AttrDef for type + */ + public function get($type) + { + // determine if there is any extra info tacked on + if (strpos($type, '#') !== false) { + list($type, $string) = explode('#', $type, 2); + } else { + $string = ''; + } + + if (!isset($this->info[$type])) { + trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR); + return; + } + return $this->info[$type]->make($string); + } + + /** + * Sets a new implementation for a type + * @param string $type String type name + * @param HTMLPurifier_AttrDef $impl Object AttrDef for type + */ + public function set($type, $impl) + { + $this->info[$type] = $impl; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php new file mode 100644 index 00000000000..f97dc93eddb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php @@ -0,0 +1,178 @@ +getHTMLDefinition(); + $e =& $context->get('ErrorCollector', true); + + // initialize IDAccumulator if necessary + $ok =& $context->get('IDAccumulator', true); + if (!$ok) { + $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); + $context->register('IDAccumulator', $id_accumulator); + } + + // initialize CurrentToken if necessary + $current_token =& $context->get('CurrentToken', true); + if (!$current_token) { + $context->register('CurrentToken', $token); + } + + if (!$token instanceof HTMLPurifier_Token_Start && + !$token instanceof HTMLPurifier_Token_Empty + ) { + return; + } + + // create alias to global definition array, see also $defs + // DEFINITION CALL + $d_defs = $definition->info_global_attr; + + // don't update token until the very end, to ensure an atomic update + $attr = $token->attr; + + // do global transformations (pre) + // nothing currently utilizes this + foreach ($definition->info_attr_transform_pre as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + } + + // do local transformations only applicable to this element (pre) + // ex.

to

+ foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + } + + // create alias to this element's attribute definition array, see + // also $d_defs (global attribute definition array) + // DEFINITION CALL + $defs = $definition->info[$token->name]->attr; + + $attr_key = false; + $context->register('CurrentAttr', $attr_key); + + // iterate through all the attribute keypairs + // Watch out for name collisions: $key has previously been used + foreach ($attr as $attr_key => $value) { + + // call the definition + if (isset($defs[$attr_key])) { + // there is a local definition defined + if ($defs[$attr_key] === false) { + // We've explicitly been told not to allow this element. + // This is usually when there's a global definition + // that must be overridden. + // Theoretically speaking, we could have a + // AttrDef_DenyAll, but this is faster! + $result = false; + } else { + // validate according to the element's definition + $result = $defs[$attr_key]->validate( + $value, + $config, + $context + ); + } + } elseif (isset($d_defs[$attr_key])) { + // there is a global definition defined, validate according + // to the global definition + $result = $d_defs[$attr_key]->validate( + $value, + $config, + $context + ); + } else { + // system never heard of the attribute? DELETE! + $result = false; + } + + // put the results into effect + if ($result === false || $result === null) { + // this is a generic error message that should replaced + // with more specific ones when possible + if ($e) { + $e->send(E_ERROR, 'AttrValidator: Attribute removed'); + } + + // remove the attribute + unset($attr[$attr_key]); + } elseif (is_string($result)) { + // generally, if a substitution is happening, there + // was some sort of implicit correction going on. We'll + // delegate it to the attribute classes to say exactly what. + + // simple substitution + $attr[$attr_key] = $result; + } else { + // nothing happens + } + + // we'd also want slightly more complicated substitution + // involving an array as the return value, + // although we're not sure how colliding attributes would + // resolve (certain ones would be completely overriden, + // others would prepend themselves). + } + + $context->destroy('CurrentAttr'); + + // post transforms + + // global (error reporting untested) + foreach ($definition->info_attr_transform_post as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + } + + // local (error reporting untested) + foreach ($definition->info[$token->name]->attr_transform_post as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + } + + $token->attr = $attr; + + // destroy CurrentToken if we made it ourselves + if (!$current_token) { + $context->destroy('CurrentToken'); + } + + } + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php new file mode 100644 index 00000000000..707122bb296 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php @@ -0,0 +1,124 @@ + +if (!defined('PHP_EOL')) { + switch (strtoupper(substr(PHP_OS, 0, 3))) { + case 'WIN': + define('PHP_EOL', "\r\n"); + break; + case 'DAR': + define('PHP_EOL', "\r"); + break; + default: + define('PHP_EOL', "\n"); + } +} + +/** + * Bootstrap class that contains meta-functionality for HTML Purifier such as + * the autoload function. + * + * @note + * This class may be used without any other files from HTML Purifier. + */ +class HTMLPurifier_Bootstrap +{ + + /** + * Autoload function for HTML Purifier + * @param string $class Class to load + * @return bool + */ + public static function autoload($class) + { + $file = HTMLPurifier_Bootstrap::getPath($class); + if (!$file) { + return false; + } + // Technically speaking, it should be ok and more efficient to + // just do 'require', but Antonio Parraga reports that with + // Zend extensions such as Zend debugger and APC, this invariant + // may be broken. Since we have efficient alternatives, pay + // the cost here and avoid the bug. + require_once HTMLPURIFIER_PREFIX . '/' . $file; + return true; + } + + /** + * Returns the path for a specific class. + * @param string $class Class path to get + * @return string + */ + public static function getPath($class) + { + if (strncmp('HTMLPurifier', $class, 12) !== 0) { + return false; + } + // Custom implementations + if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { + $code = str_replace('_', '-', substr($class, 22)); + $file = 'HTMLPurifier/Language/classes/' . $code . '.php'; + } else { + $file = str_replace('_', '/', $class) . '.php'; + } + if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) { + return false; + } + return $file; + } + + /** + * "Pre-registers" our autoloader on the SPL stack. + */ + public static function registerAutoload() + { + $autoload = array('HTMLPurifier_Bootstrap', 'autoload'); + if (($funcs = spl_autoload_functions()) === false) { + spl_autoload_register($autoload); + } elseif (function_exists('spl_autoload_unregister')) { + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + // prepend flag exists, no need for shenanigans + spl_autoload_register($autoload, true, true); + } else { + $buggy = version_compare(PHP_VERSION, '5.2.11', '<'); + $compat = version_compare(PHP_VERSION, '5.1.2', '<=') && + version_compare(PHP_VERSION, '5.1.0', '>='); + foreach ($funcs as $func) { + if ($buggy && is_array($func)) { + // :TRICKY: There are some compatibility issues and some + // places where we need to error out + $reflector = new ReflectionMethod($func[0], $func[1]); + if (!$reflector->isStatic()) { + throw new Exception( + 'HTML Purifier autoloader registrar is not compatible + with non-static object methods due to PHP Bug #44144; + Please do not use HTMLPurifier.autoload.php (or any + file that includes this file); instead, place the code: + spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\')) + after your own autoloaders.' + ); + } + // Suprisingly, spl_autoload_register supports the + // Class::staticMethod callback format, although call_user_func doesn't + if ($compat) { + $func = implode('::', $func); + } + } + spl_autoload_unregister($func); + } + spl_autoload_register($autoload); + foreach ($funcs as $func) { + spl_autoload_register($func); + } + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php new file mode 100644 index 00000000000..21f1a5899d8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php @@ -0,0 +1,533 @@ +info['text-align'] = new HTMLPurifier_AttrDef_Enum( + array('left', 'right', 'center', 'justify'), + false + ); + + $border_style = + $this->info['border-bottom-style'] = + $this->info['border-right-style'] = + $this->info['border-left-style'] = + $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( + array( + 'none', + 'hidden', + 'dotted', + 'dashed', + 'solid', + 'double', + 'groove', + 'ridge', + 'inset', + 'outset' + ), + false + ); + + $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); + + $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( + array('none', 'left', 'right', 'both'), + false + ); + $this->info['float'] = new HTMLPurifier_AttrDef_Enum( + array('none', 'left', 'right'), + false + ); + $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( + array('normal', 'italic', 'oblique'), + false + ); + $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( + array('normal', 'small-caps'), + false + ); + + $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('none')), + new HTMLPurifier_AttrDef_CSS_URI() + ) + ); + + $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( + array('inside', 'outside'), + false + ); + $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( + array( + 'disc', + 'circle', + 'square', + 'decimal', + 'lower-roman', + 'upper-roman', + 'lower-alpha', + 'upper-alpha', + 'none' + ), + false + ); + $this->info['list-style-image'] = $uri_or_none; + + $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); + + $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( + array('capitalize', 'uppercase', 'lowercase', 'none'), + false + ); + $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); + + $this->info['background-image'] = $uri_or_none; + $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( + array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') + ); + $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( + array('scroll', 'fixed') + ); + $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); + + $border_color = + $this->info['border-top-color'] = + $this->info['border-bottom-color'] = + $this->info['border-left-color'] = + $this->info['border-right-color'] = + $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('transparent')), + new HTMLPurifier_AttrDef_CSS_Color() + ) + ); + + $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); + + $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); + + $border_width = + $this->info['border-top-width'] = + $this->info['border-bottom-width'] = + $this->info['border-left-width'] = + $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), + new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative + ) + ); + + $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); + + $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum( + array( + 'xx-small', + 'x-small', + 'small', + 'medium', + 'large', + 'x-large', + 'xx-large', + 'larger', + 'smaller' + ) + ), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + ) + ); + + $margin = + $this->info['margin-top'] = + $this->info['margin-bottom'] = + $this->info['margin-left'] = + $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')) + ) + ); + + $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); + + // non-negative + $padding = + $this->info['padding-top'] = + $this->info['padding-bottom'] = + $this->info['padding-left'] = + $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + ) + ); + + $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); + + $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + ) + ); + + $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true), + new HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit')) + ) + ); + $trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true), + new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')) + ) + ); + $trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true), + new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')) + ) + ); + $max = $config->get('CSS.MaxImgLength'); + + $this->info['width'] = + $this->info['height'] = + $max === null ? + $trusted_wh : + new HTMLPurifier_AttrDef_Switch( + 'img', + // For img tags: + new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0', $max), + new HTMLPurifier_AttrDef_Enum(array('auto')) + ) + ), + // For everyone else: + $trusted_wh + ); + $this->info['min-width'] = + $this->info['min-height'] = + $max === null ? + $trusted_min_wh : + new HTMLPurifier_AttrDef_Switch( + 'img', + // For img tags: + new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0', $max), + new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')) + ) + ), + // For everyone else: + $trusted_min_wh + ); + $this->info['max-width'] = + $this->info['max-height'] = + $max === null ? + $trusted_max_wh : + new HTMLPurifier_AttrDef_Switch( + 'img', + // For img tags: + new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0', $max), + new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')) + ) + ), + // For everyone else: + $trusted_max_wh + ); + + $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); + + $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); + + // this could use specialized code + $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( + array( + 'normal', + 'bold', + 'bolder', + 'lighter', + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900' + ), + false + ); + + // MUST be called after other font properties, as it references + // a CSSDefinition object + $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config); + + // same here + $this->info['border'] = + $this->info['border-bottom'] = + $this->info['border-top'] = + $this->info['border-left'] = + $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); + + $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum( + array('collapse', 'separate') + ); + + $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum( + array('top', 'bottom') + ); + + $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum( + array('auto', 'fixed') + ); + + $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum( + array( + 'baseline', + 'sub', + 'super', + 'top', + 'text-top', + 'middle', + 'bottom', + 'text-bottom' + ) + ), + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + ) + ); + + $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); + + // These CSS properties don't work on many browsers, but we live + // in THE FUTURE! + $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum( + array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line') + ); + + if ($config->get('CSS.Proprietary')) { + $this->doSetupProprietary($config); + } + + if ($config->get('CSS.AllowTricky')) { + $this->doSetupTricky($config); + } + + if ($config->get('CSS.Trusted')) { + $this->doSetupTrusted($config); + } + + $allow_important = $config->get('CSS.AllowImportant'); + // wrap all attr-defs with decorator that handles !important + foreach ($this->info as $k => $v) { + $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); + } + + $this->setupConfigStuff($config); + } + + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupProprietary($config) + { + // Internet Explorer only scrollbar colors + $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + + // vendor specific prefixes of opacity + $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + + // only opacity, for now + $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); + + // more CSS3 + $this->info['page-break-after'] = + $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum( + array( + 'auto', + 'always', + 'avoid', + 'left', + 'right' + ) + ); + $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid')); + + $border_radius = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative + new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative + )); + + $this->info['border-top-left-radius'] = + $this->info['border-top-right-radius'] = + $this->info['border-bottom-right-radius'] = + $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2); + // TODO: support SLASH syntax + $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4); + + } + + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupTricky($config) + { + $this->info['display'] = new HTMLPurifier_AttrDef_Enum( + array( + 'inline', + 'block', + 'list-item', + 'run-in', + 'compact', + 'marker', + 'table', + 'inline-block', + 'inline-table', + 'table-row-group', + 'table-header-group', + 'table-footer-group', + 'table-row', + 'table-column-group', + 'table-column', + 'table-cell', + 'table-caption', + 'none' + ) + ); + $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum( + array('visible', 'hidden', 'collapse') + ); + $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); + $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + } + + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupTrusted($config) + { + $this->info['position'] = new HTMLPurifier_AttrDef_Enum( + array('static', 'relative', 'absolute', 'fixed') + ); + $this->info['top'] = + $this->info['left'] = + $this->info['right'] = + $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + ) + ); + $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Integer(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + ) + ); + } + + /** + * Performs extra config-based processing. Based off of + * HTMLPurifier_HTMLDefinition. + * @param HTMLPurifier_Config $config + * @todo Refactor duplicate elements into common class (probably using + * composition, not inheritance). + */ + protected function setupConfigStuff($config) + { + // setup allowed elements + $support = "(for information on implementing this, see the " . + "support forums) "; + $allowed_properties = $config->get('CSS.AllowedProperties'); + if ($allowed_properties !== null) { + foreach ($this->info as $name => $d) { + if (!isset($allowed_properties[$name])) { + unset($this->info[$name]); + } + unset($allowed_properties[$name]); + } + // emit errors + foreach ($allowed_properties as $name => $d) { + // :TODO: Is this htmlspecialchars() call really necessary? + $name = htmlspecialchars($name); + trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); + } + } + + $forbidden_properties = $config->get('CSS.ForbiddenProperties'); + if ($forbidden_properties !== null) { + foreach ($this->info as $name => $d) { + if (isset($forbidden_properties[$name])) { + unset($this->info[$name]); + } + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php new file mode 100644 index 00000000000..8eb17b82e15 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php @@ -0,0 +1,52 @@ +elements; + } + + /** + * Validates nodes according to definition and returns modification. + * + * @param HTMLPurifier_Node[] $children Array of HTMLPurifier_Node + * @param HTMLPurifier_Config $config HTMLPurifier_Config object + * @param HTMLPurifier_Context $context HTMLPurifier_Context object + * @return bool|array true to leave nodes as is, false to remove parent node, array of replacement children + */ + abstract public function validateChildren($children, $config, $context); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php new file mode 100644 index 00000000000..7439be26b64 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php @@ -0,0 +1,67 @@ +inline = new HTMLPurifier_ChildDef_Optional($inline); + $this->block = new HTMLPurifier_ChildDef_Optional($block); + $this->elements = $this->block->elements; + } + + /** + * @param HTMLPurifier_Node[] $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function validateChildren($children, $config, $context) + { + if ($context->get('IsInline') === false) { + return $this->block->validateChildren( + $children, + $config, + $context + ); + } else { + return $this->inline->validateChildren( + $children, + $config, + $context + ); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php new file mode 100644 index 00000000000..f515888a1d4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php @@ -0,0 +1,102 @@ +dtd_regex = $dtd_regex; + $this->_compileRegex(); + } + + /** + * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex) + */ + protected function _compileRegex() + { + $raw = str_replace(' ', '', $this->dtd_regex); + if ($raw[0] != '(') { + $raw = "($raw)"; + } + $el = '[#a-zA-Z0-9_.-]+'; + $reg = $raw; + + // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M + // DOING! Seriously: if there's problems, please report them. + + // collect all elements into the $elements array + preg_match_all("/$el/", $reg, $matches); + foreach ($matches[0] as $match) { + $this->elements[$match] = true; + } + + // setup all elements as parentheticals with leading commas + $reg = preg_replace("/$el/", '(,\\0)', $reg); + + // remove commas when they were not solicited + $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg); + + // remove all non-paranthetical commas: they are handled by first regex + $reg = preg_replace("/,\(/", '(', $reg); + + $this->_pcre_regex = $reg; + } + + /** + * @param HTMLPurifier_Node[] $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function validateChildren($children, $config, $context) + { + $list_of_children = ''; + $nesting = 0; // depth into the nest + foreach ($children as $node) { + if (!empty($node->is_whitespace)) { + continue; + } + $list_of_children .= $node->name . ','; + } + // add leading comma to deal with stray comma declarations + $list_of_children = ',' . rtrim($list_of_children, ','); + $okay = + preg_match( + '/^,?' . $this->_pcre_regex . '$/', + $list_of_children + ); + return (bool)$okay; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php new file mode 100644 index 00000000000..a8a6cbdd2c4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php @@ -0,0 +1,38 @@ + true, 'ul' => true, 'ol' => true); + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + // Flag for subclasses + $this->whitespace = false; + + // if there are no tokens, delete parent node + if (empty($children)) { + return false; + } + + // if li is not allowed, delete parent node + if (!isset($config->getHTMLDefinition()->info['li'])) { + trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING); + return false; + } + + // the new set of children + $result = array(); + + // a little sanity check to make sure it's not ALL whitespace + $all_whitespace = true; + + $current_li = null; + + foreach ($children as $node) { + if (!empty($node->is_whitespace)) { + $result[] = $node; + continue; + } + $all_whitespace = false; // phew, we're not talking about whitespace + + if ($node->name === 'li') { + // good + $current_li = $node; + $result[] = $node; + } else { + // we want to tuck this into the previous li + // Invariant: we expect the node to be ol/ul + // ToDo: Make this more robust in the case of not ol/ul + // by distinguishing between existing li and li created + // to handle non-list elements; non-list elements should + // not be appended to an existing li; only li created + // for non-list. This distinction is not currently made. + if ($current_li === null) { + $current_li = new HTMLPurifier_Node_Element('li'); + $result[] = $current_li; + } + $current_li->children[] = $node; + $current_li->empty = false; // XXX fascinating! Check for this error elsewhere ToDo + } + } + if (empty($result)) { + return false; + } + if ($all_whitespace) { + return false; + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php new file mode 100644 index 00000000000..b9468063b1a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php @@ -0,0 +1,45 @@ +whitespace) { + return $children; + } else { + return array(); + } + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php new file mode 100644 index 00000000000..0d1c8f5f397 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php @@ -0,0 +1,118 @@ + $x) { + $elements[$i] = true; + if (empty($i)) { + unset($elements[$i]); + } // remove blank + } + } + $this->elements = $elements; + } + + /** + * @type bool + */ + public $allow_empty = false; + + /** + * @type string + */ + public $type = 'required'; + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + // Flag for subclasses + $this->whitespace = false; + + // if there are no tokens, delete parent node + if (empty($children)) { + return false; + } + + // the new set of children + $result = array(); + + // whether or not parsed character data is allowed + // this controls whether or not we silently drop a tag + // or generate escaped HTML from it + $pcdata_allowed = isset($this->elements['#PCDATA']); + + // a little sanity check to make sure it's not ALL whitespace + $all_whitespace = true; + + $stack = array_reverse($children); + while (!empty($stack)) { + $node = array_pop($stack); + if (!empty($node->is_whitespace)) { + $result[] = $node; + continue; + } + $all_whitespace = false; // phew, we're not talking about whitespace + + if (!isset($this->elements[$node->name])) { + // special case text + // XXX One of these ought to be redundant or something + if ($pcdata_allowed && $node instanceof HTMLPurifier_Node_Text) { + $result[] = $node; + continue; + } + // spill the child contents in + // ToDo: Make configurable + if ($node instanceof HTMLPurifier_Node_Element) { + for ($i = count($node->children) - 1; $i >= 0; $i--) { + $stack[] = $node->children[$i]; + } + continue; + } + continue; + } + $result[] = $node; + } + if (empty($result)) { + return false; + } + if ($all_whitespace) { + $this->whitespace = true; + return false; + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php new file mode 100644 index 00000000000..3270a46e1bf --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php @@ -0,0 +1,110 @@ +init($config); + return $this->fake_elements; + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + $this->init($config); + + // trick the parent class into thinking it allows more + $this->elements = $this->fake_elements; + $result = parent::validateChildren($children, $config, $context); + $this->elements = $this->real_elements; + + if ($result === false) { + return array(); + } + if ($result === true) { + $result = $children; + } + + $def = $config->getHTMLDefinition(); + $block_wrap_name = $def->info_block_wrapper; + $block_wrap = false; + $ret = array(); + + foreach ($result as $node) { + if ($block_wrap === false) { + if (($node instanceof HTMLPurifier_Node_Text && !$node->is_whitespace) || + ($node instanceof HTMLPurifier_Node_Element && !isset($this->elements[$node->name]))) { + $block_wrap = new HTMLPurifier_Node_Element($def->info_block_wrapper); + $ret[] = $block_wrap; + } + } else { + if ($node instanceof HTMLPurifier_Node_Element && isset($this->elements[$node->name])) { + $block_wrap = false; + + } + } + if ($block_wrap) { + $block_wrap->children[] = $node; + } else { + $ret[] = $node; + } + } + return $ret; + } + + /** + * @param HTMLPurifier_Config $config + */ + private function init($config) + { + if (!$this->init) { + $def = $config->getHTMLDefinition(); + // allow all inline elements + $this->real_elements = $this->elements; + $this->fake_elements = $def->info_content_sets['Flow']; + $this->fake_elements['#PCDATA'] = true; + $this->init = true; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php new file mode 100644 index 00000000000..cb6b3e6cdc2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php @@ -0,0 +1,224 @@ + true, + 'tbody' => true, + 'thead' => true, + 'tfoot' => true, + 'caption' => true, + 'colgroup' => true, + 'col' => true + ); + + public function __construct() + { + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + if (empty($children)) { + return false; + } + + // only one of these elements is allowed in a table + $caption = false; + $thead = false; + $tfoot = false; + + // whitespace + $initial_ws = array(); + $after_caption_ws = array(); + $after_thead_ws = array(); + $after_tfoot_ws = array(); + + // as many of these as you want + $cols = array(); + $content = array(); + + $tbody_mode = false; // if true, then we need to wrap any stray + // s with a . + + $ws_accum =& $initial_ws; + + foreach ($children as $node) { + if ($node instanceof HTMLPurifier_Node_Comment) { + $ws_accum[] = $node; + continue; + } + switch ($node->name) { + case 'tbody': + $tbody_mode = true; + // fall through + case 'tr': + $content[] = $node; + $ws_accum =& $content; + break; + case 'caption': + // there can only be one caption! + if ($caption !== false) break; + $caption = $node; + $ws_accum =& $after_caption_ws; + break; + case 'thead': + $tbody_mode = true; + // XXX This breaks rendering properties with + // Firefox, which never floats a to + // the top. Ever. (Our scheme will float the + // first to the top.) So maybe + // s that are not first should be + // turned into ? Very tricky, indeed. + if ($thead === false) { + $thead = $node; + $ws_accum =& $after_thead_ws; + } else { + // Oops, there's a second one! What + // should we do? Current behavior is to + // transmutate the first and last entries into + // tbody tags, and then put into content. + // Maybe a better idea is to *attach + // it* to the existing thead or tfoot? + // We don't do this, because Firefox + // doesn't float an extra tfoot to the + // bottom like it does for the first one. + $node->name = 'tbody'; + $content[] = $node; + $ws_accum =& $content; + } + break; + case 'tfoot': + // see above for some aveats + $tbody_mode = true; + if ($tfoot === false) { + $tfoot = $node; + $ws_accum =& $after_tfoot_ws; + } else { + $node->name = 'tbody'; + $content[] = $node; + $ws_accum =& $content; + } + break; + case 'colgroup': + case 'col': + $cols[] = $node; + $ws_accum =& $cols; + break; + case '#PCDATA': + // How is whitespace handled? We treat is as sticky to + // the *end* of the previous element. So all of the + // nonsense we have worked on is to keep things + // together. + if (!empty($node->is_whitespace)) { + $ws_accum[] = $node; + } + break; + } + } + + if (empty($content)) { + return false; + } + + $ret = $initial_ws; + if ($caption !== false) { + $ret[] = $caption; + $ret = array_merge($ret, $after_caption_ws); + } + if ($cols !== false) { + $ret = array_merge($ret, $cols); + } + if ($thead !== false) { + $ret[] = $thead; + $ret = array_merge($ret, $after_thead_ws); + } + if ($tfoot !== false) { + $ret[] = $tfoot; + $ret = array_merge($ret, $after_tfoot_ws); + } + + if ($tbody_mode) { + // we have to shuffle tr into tbody + $current_tr_tbody = null; + + foreach($content as $node) { + switch ($node->name) { + case 'tbody': + $current_tr_tbody = null; + $ret[] = $node; + break; + case 'tr': + if ($current_tr_tbody === null) { + $current_tr_tbody = new HTMLPurifier_Node_Element('tbody'); + $ret[] = $current_tr_tbody; + } + $current_tr_tbody->children[] = $node; + break; + case '#PCDATA': + //assert($node->is_whitespace); + if ($current_tr_tbody === null) { + $ret[] = $node; + } else { + $current_tr_tbody->children[] = $node; + } + break; + } + } + } else { + $ret = array_merge($ret, $content); + } + + return $ret; + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php new file mode 100644 index 00000000000..3133d8a4ff6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php @@ -0,0 +1,920 @@ +defaultPlist; + $this->plist = new HTMLPurifier_PropertyList($parent); + $this->def = $definition; // keep a copy around for checking + $this->parser = new HTMLPurifier_VarParser_Flexible(); + } + + /** + * Convenience constructor that creates a config object based on a mixed var + * @param mixed $config Variable that defines the state of the config + * object. Can be: a HTMLPurifier_Config() object, + * an array of directives based on loadArray(), + * or a string filename of an ini file. + * @param HTMLPurifier_ConfigSchema $schema Schema object + * @return HTMLPurifier_Config Configured object + */ + public static function create($config, $schema = null) + { + if ($config instanceof HTMLPurifier_Config) { + // pass-through + return $config; + } + if (!$schema) { + $ret = HTMLPurifier_Config::createDefault(); + } else { + $ret = new HTMLPurifier_Config($schema); + } + if (is_string($config)) { + $ret->loadIni($config); + } elseif (is_array($config)) $ret->loadArray($config); + return $ret; + } + + /** + * Creates a new config object that inherits from a previous one. + * @param HTMLPurifier_Config $config Configuration object to inherit from. + * @return HTMLPurifier_Config object with $config as its parent. + */ + public static function inherit(HTMLPurifier_Config $config) + { + return new HTMLPurifier_Config($config->def, $config->plist); + } + + /** + * Convenience constructor that creates a default configuration object. + * @return HTMLPurifier_Config default object. + */ + public static function createDefault() + { + $definition = HTMLPurifier_ConfigSchema::instance(); + $config = new HTMLPurifier_Config($definition); + return $config; + } + + /** + * Retrieves a value from the configuration. + * + * @param string $key String key + * @param mixed $a + * + * @return mixed + */ + public function get($key, $a = null) + { + if ($a !== null) { + $this->triggerError( + "Using deprecated API: use \$config->get('$key.$a') instead", + E_USER_WARNING + ); + $key = "$key.$a"; + } + if (!$this->finalized) { + $this->autoFinalize(); + } + if (!isset($this->def->info[$key])) { + // can't add % due to SimpleTest bug + $this->triggerError( + 'Cannot retrieve value of undefined directive ' . htmlspecialchars($key), + E_USER_WARNING + ); + return; + } + if (isset($this->def->info[$key]->isAlias)) { + $d = $this->def->info[$key]; + $this->triggerError( + 'Cannot get value from aliased directive, use real name ' . $d->key, + E_USER_ERROR + ); + return; + } + if ($this->lock) { + list($ns) = explode('.', $key); + if ($ns !== $this->lock) { + $this->triggerError( + 'Cannot get value of namespace ' . $ns . ' when lock for ' . + $this->lock . + ' is active, this probably indicates a Definition setup method ' . + 'is accessing directives that are not within its namespace', + E_USER_ERROR + ); + return; + } + } + return $this->plist->get($key); + } + + /** + * Retrieves an array of directives to values from a given namespace + * + * @param string $namespace String namespace + * + * @return array + */ + public function getBatch($namespace) + { + if (!$this->finalized) { + $this->autoFinalize(); + } + $full = $this->getAll(); + if (!isset($full[$namespace])) { + $this->triggerError( + 'Cannot retrieve undefined namespace ' . + htmlspecialchars($namespace), + E_USER_WARNING + ); + return; + } + return $full[$namespace]; + } + + /** + * Returns a SHA-1 signature of a segment of the configuration object + * that uniquely identifies that particular configuration + * + * @param string $namespace Namespace to get serial for + * + * @return string + * @note Revision is handled specially and is removed from the batch + * before processing! + */ + public function getBatchSerial($namespace) + { + if (empty($this->serials[$namespace])) { + $batch = $this->getBatch($namespace); + unset($batch['DefinitionRev']); + $this->serials[$namespace] = sha1(serialize($batch)); + } + return $this->serials[$namespace]; + } + + /** + * Returns a SHA-1 signature for the entire configuration object + * that uniquely identifies that particular configuration + * + * @return string + */ + public function getSerial() + { + if (empty($this->serial)) { + $this->serial = sha1(serialize($this->getAll())); + } + return $this->serial; + } + + /** + * Retrieves all directives, organized by namespace + * + * @warning This is a pretty inefficient function, avoid if you can + */ + public function getAll() + { + if (!$this->finalized) { + $this->autoFinalize(); + } + $ret = array(); + foreach ($this->plist->squash() as $name => $value) { + list($ns, $key) = explode('.', $name, 2); + $ret[$ns][$key] = $value; + } + return $ret; + } + + /** + * Sets a value to configuration. + * + * @param string $key key + * @param mixed $value value + * @param mixed $a + */ + public function set($key, $value, $a = null) + { + if (strpos($key, '.') === false) { + $namespace = $key; + $directive = $value; + $value = $a; + $key = "$key.$directive"; + $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE); + } else { + list($namespace) = explode('.', $key); + } + if ($this->isFinalized('Cannot set directive after finalization')) { + return; + } + if (!isset($this->def->info[$key])) { + $this->triggerError( + 'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value', + E_USER_WARNING + ); + return; + } + $def = $this->def->info[$key]; + + if (isset($def->isAlias)) { + if ($this->aliasMode) { + $this->triggerError( + 'Double-aliases not allowed, please fix '. + 'ConfigSchema bug with' . $key, + E_USER_ERROR + ); + return; + } + $this->aliasMode = true; + $this->set($def->key, $value); + $this->aliasMode = false; + $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE); + return; + } + + // Raw type might be negative when using the fully optimized form + // of stdClass, which indicates allow_null == true + $rtype = is_int($def) ? $def : $def->type; + if ($rtype < 0) { + $type = -$rtype; + $allow_null = true; + } else { + $type = $rtype; + $allow_null = isset($def->allow_null); + } + + try { + $value = $this->parser->parse($value, $type, $allow_null); + } catch (HTMLPurifier_VarParserException $e) { + $this->triggerError( + 'Value for ' . $key . ' is of invalid type, should be ' . + HTMLPurifier_VarParser::getTypeName($type), + E_USER_WARNING + ); + return; + } + if (is_string($value) && is_object($def)) { + // resolve value alias if defined + if (isset($def->aliases[$value])) { + $value = $def->aliases[$value]; + } + // check to see if the value is allowed + if (isset($def->allowed) && !isset($def->allowed[$value])) { + $this->triggerError( + 'Value not supported, valid values are: ' . + $this->_listify($def->allowed), + E_USER_WARNING + ); + return; + } + } + $this->plist->set($key, $value); + + // reset definitions if the directives they depend on changed + // this is a very costly process, so it's discouraged + // with finalization + if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') { + $this->definitions[$namespace] = null; + } + + $this->serials[$namespace] = false; + } + + /** + * Convenience function for error reporting + * + * @param array $lookup + * + * @return string + */ + private function _listify($lookup) + { + $list = array(); + foreach ($lookup as $name => $b) { + $list[] = $name; + } + return implode(', ', $list); + } + + /** + * Retrieves object reference to the HTML definition. + * + * @param bool $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + * @param bool $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawHTMLDefinition, which is more explicitly + * named, instead. + * + * @return HTMLPurifier_HTMLDefinition|null + */ + public function getHTMLDefinition($raw = false, $optimized = false) + { + return $this->getDefinition('HTML', $raw, $optimized); + } + + /** + * Retrieves object reference to the CSS definition + * + * @param bool $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + * @param bool $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawCSSDefinition, which is more explicitly + * named, instead. + * + * @return HTMLPurifier_CSSDefinition|null + */ + public function getCSSDefinition($raw = false, $optimized = false) + { + return $this->getDefinition('CSS', $raw, $optimized); + } + + /** + * Retrieves object reference to the URI definition + * + * @param bool $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + * @param bool $optimized If true, this method may return null, to + * indicate that a cached version of the modified + * definition object is available and no further edits + * are necessary. Consider using + * maybeGetRawURIDefinition, which is more explicitly + * named, instead. + * + * @return HTMLPurifier_URIDefinition|null + */ + public function getURIDefinition($raw = false, $optimized = false) + { + return $this->getDefinition('URI', $raw, $optimized); + } + + /** + * Retrieves a definition + * + * @param string $type Type of definition: HTML, CSS, etc + * @param bool $raw Whether or not definition should be returned raw + * @param bool $optimized Only has an effect when $raw is true. Whether + * or not to return null if the result is already present in + * the cache. This is off by default for backwards + * compatibility reasons, but you need to do things this + * way in order to ensure that caching is done properly. + * Check out enduser-customize.html for more details. + * We probably won't ever change this default, as much as the + * maybe semantics is the "right thing to do." + * + * @throws HTMLPurifier_Exception + * @return HTMLPurifier_Definition|null + */ + public function getDefinition($type, $raw = false, $optimized = false) + { + if ($optimized && !$raw) { + throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false"); + } + if (!$this->finalized) { + $this->autoFinalize(); + } + // temporarily suspend locks, so we can handle recursive definition calls + $lock = $this->lock; + $this->lock = null; + $factory = HTMLPurifier_DefinitionCacheFactory::instance(); + $cache = $factory->create($type, $this); + $this->lock = $lock; + if (!$raw) { + // full definition + // --------------- + // check if definition is in memory + if (!empty($this->definitions[$type])) { + $def = $this->definitions[$type]; + // check if the definition is setup + if ($def->setup) { + return $def; + } else { + $def->setup($this); + if ($def->optimized) { + $cache->add($def, $this); + } + return $def; + } + } + // check if definition is in cache + $def = $cache->get($this); + if ($def) { + // definition in cache, save to memory and return it + $this->definitions[$type] = $def; + return $def; + } + // initialize it + $def = $this->initDefinition($type); + // set it up + $this->lock = $type; + $def->setup($this); + $this->lock = null; + // save in cache + $cache->add($def, $this); + // return it + return $def; + } else { + // raw definition + // -------------- + // check preconditions + $def = null; + if ($optimized) { + if (is_null($this->get($type . '.DefinitionID'))) { + // fatally error out if definition ID not set + throw new HTMLPurifier_Exception( + "Cannot retrieve raw version without specifying %$type.DefinitionID" + ); + } + } + if (!empty($this->definitions[$type])) { + $def = $this->definitions[$type]; + if ($def->setup && !$optimized) { + $extra = $this->chatty ? + " (try moving this code block earlier in your initialization)" : + ""; + throw new HTMLPurifier_Exception( + "Cannot retrieve raw definition after it has already been setup" . + $extra + ); + } + if ($def->optimized === null) { + $extra = $this->chatty ? " (try flushing your cache)" : ""; + throw new HTMLPurifier_Exception( + "Optimization status of definition is unknown" . $extra + ); + } + if ($def->optimized !== $optimized) { + $msg = $optimized ? "optimized" : "unoptimized"; + $extra = $this->chatty ? + " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)" + : ""; + throw new HTMLPurifier_Exception( + "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra + ); + } + } + // check if definition was in memory + if ($def) { + if ($def->setup) { + // invariant: $optimized === true (checked above) + return null; + } else { + return $def; + } + } + // if optimized, check if definition was in cache + // (because we do the memory check first, this formulation + // is prone to cache slamming, but I think + // guaranteeing that either /all/ of the raw + // setup code or /none/ of it is run is more important.) + if ($optimized) { + // This code path only gets run once; once we put + // something in $definitions (which is guaranteed by the + // trailing code), we always short-circuit above. + $def = $cache->get($this); + if ($def) { + // save the full definition for later, but don't + // return it yet + $this->definitions[$type] = $def; + return null; + } + } + // check invariants for creation + if (!$optimized) { + if (!is_null($this->get($type . '.DefinitionID'))) { + if ($this->chatty) { + $this->triggerError( + 'Due to a documentation error in previous version of HTML Purifier, your ' . + 'definitions are not being cached. If this is OK, you can remove the ' . + '%$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, ' . + 'modify your code to use maybeGetRawDefinition, and test if the returned ' . + 'value is null before making any edits (if it is null, that means that a ' . + 'cached version is available, and no raw operations are necessary). See ' . + '' . + 'Customize for more details', + E_USER_WARNING + ); + } else { + $this->triggerError( + "Useless DefinitionID declaration", + E_USER_WARNING + ); + } + } + } + // initialize it + $def = $this->initDefinition($type); + $def->optimized = $optimized; + return $def; + } + throw new HTMLPurifier_Exception("The impossible happened!"); + } + + /** + * Initialise definition + * + * @param string $type What type of definition to create + * + * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition + * @throws HTMLPurifier_Exception + */ + private function initDefinition($type) + { + // quick checks failed, let's create the object + if ($type == 'HTML') { + $def = new HTMLPurifier_HTMLDefinition(); + } elseif ($type == 'CSS') { + $def = new HTMLPurifier_CSSDefinition(); + } elseif ($type == 'URI') { + $def = new HTMLPurifier_URIDefinition(); + } else { + throw new HTMLPurifier_Exception( + "Definition of $type type not supported" + ); + } + $this->definitions[$type] = $def; + return $def; + } + + public function maybeGetRawDefinition($name) + { + return $this->getDefinition($name, true, true); + } + + /** + * @return HTMLPurifier_HTMLDefinition|null + */ + public function maybeGetRawHTMLDefinition() + { + return $this->getDefinition('HTML', true, true); + } + + /** + * @return HTMLPurifier_CSSDefinition|null + */ + public function maybeGetRawCSSDefinition() + { + return $this->getDefinition('CSS', true, true); + } + + /** + * @return HTMLPurifier_URIDefinition|null + */ + public function maybeGetRawURIDefinition() + { + return $this->getDefinition('URI', true, true); + } + + /** + * Loads configuration values from an array with the following structure: + * Namespace.Directive => Value + * + * @param array $config_array Configuration associative array + */ + public function loadArray($config_array) + { + if ($this->isFinalized('Cannot load directives after finalization')) { + return; + } + foreach ($config_array as $key => $value) { + $key = str_replace('_', '.', $key); + if (strpos($key, '.') !== false) { + $this->set($key, $value); + } else { + $namespace = $key; + $namespace_values = $value; + foreach ($namespace_values as $directive => $value2) { + $this->set($namespace .'.'. $directive, $value2); + } + } + } + } + + /** + * Returns a list of array(namespace, directive) for all directives + * that are allowed in a web-form context as per an allowed + * namespaces/directives list. + * + * @param array $allowed List of allowed namespaces/directives + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return array + */ + public static function getAllowedDirectivesForForm($allowed, $schema = null) + { + if (!$schema) { + $schema = HTMLPurifier_ConfigSchema::instance(); + } + if ($allowed !== true) { + if (is_string($allowed)) { + $allowed = array($allowed); + } + $allowed_ns = array(); + $allowed_directives = array(); + $blacklisted_directives = array(); + foreach ($allowed as $ns_or_directive) { + if (strpos($ns_or_directive, '.') !== false) { + // directive + if ($ns_or_directive[0] == '-') { + $blacklisted_directives[substr($ns_or_directive, 1)] = true; + } else { + $allowed_directives[$ns_or_directive] = true; + } + } else { + // namespace + $allowed_ns[$ns_or_directive] = true; + } + } + } + $ret = array(); + foreach ($schema->info as $key => $def) { + list($ns, $directive) = explode('.', $key, 2); + if ($allowed !== true) { + if (isset($blacklisted_directives["$ns.$directive"])) { + continue; + } + if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) { + continue; + } + } + if (isset($def->isAlias)) { + continue; + } + if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') { + continue; + } + $ret[] = array($ns, $directive); + } + return $ret; + } + + /** + * Loads configuration values from $_GET/$_POST that were posted + * via ConfigForm + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return mixed + */ + public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) + { + $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema); + $config = HTMLPurifier_Config::create($ret, $schema); + return $config; + } + + /** + * Merges in configuration values from $_GET/$_POST to object. NOT STATIC. + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix + */ + public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) + { + $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def); + $this->loadArray($ret); + } + + /** + * Prepares an array from a form into something usable for the more + * strict parts of HTMLPurifier_Config + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return array + */ + public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) + { + if ($index !== false) { + $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); + } + $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc(); + + $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema); + $ret = array(); + foreach ($allowed as $key) { + list($ns, $directive) = $key; + $skey = "$ns.$directive"; + if (!empty($array["Null_$skey"])) { + $ret[$ns][$directive] = null; + continue; + } + if (!isset($array[$skey])) { + continue; + } + $value = $mq ? stripslashes($array[$skey]) : $array[$skey]; + $ret[$ns][$directive] = $value; + } + return $ret; + } + + /** + * Loads configuration values from an ini file + * + * @param string $filename Name of ini file + */ + public function loadIni($filename) + { + if ($this->isFinalized('Cannot load directives after finalization')) { + return; + } + $array = parse_ini_file($filename, true); + $this->loadArray($array); + } + + /** + * Checks whether or not the configuration object is finalized. + * + * @param string|bool $error String error message, or false for no error + * + * @return bool + */ + public function isFinalized($error = false) + { + if ($this->finalized && $error) { + $this->triggerError($error, E_USER_ERROR); + } + return $this->finalized; + } + + /** + * Finalizes configuration only if auto finalize is on and not + * already finalized + */ + public function autoFinalize() + { + if ($this->autoFinalize) { + $this->finalize(); + } else { + $this->plist->squash(true); + } + } + + /** + * Finalizes a configuration object, prohibiting further change + */ + public function finalize() + { + $this->finalized = true; + $this->parser = null; + } + + /** + * Produces a nicely formatted error message by supplying the + * stack frame information OUTSIDE of HTMLPurifier_Config. + * + * @param string $msg An error message + * @param int $no An error number + */ + protected function triggerError($msg, $no) + { + // determine previous stack frame + $extra = ''; + if ($this->chatty) { + $trace = debug_backtrace(); + // zip(tail(trace), trace) -- but PHP is not Haskell har har + for ($i = 0, $c = count($trace); $i < $c - 1; $i++) { + // XXX this is not correct on some versions of HTML Purifier + if (isset($trace[$i + 1]['class']) && $trace[$i + 1]['class'] === 'HTMLPurifier_Config') { + continue; + } + $frame = $trace[$i]; + $extra = " invoked on line {$frame['line']} in file {$frame['file']}"; + break; + } + } + trigger_error($msg . $extra, $no); + } + + /** + * Returns a serialized form of the configuration object that can + * be reconstituted. + * + * @return string + */ + public function serialize() + { + $this->getDefinition('HTML'); + $this->getDefinition('CSS'); + $this->getDefinition('URI'); + return serialize($this); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php new file mode 100644 index 00000000000..c3fe8cd4abb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php @@ -0,0 +1,176 @@ + array( + * 'Directive' => new stdClass(), + * ) + * ) + * + * The stdClass may have the following properties: + * + * - If isAlias isn't set: + * - type: Integer type of directive, see HTMLPurifier_VarParser for definitions + * - allow_null: If set, this directive allows null values + * - aliases: If set, an associative array of value aliases to real values + * - allowed: If set, a lookup array of allowed (string) values + * - If isAlias is set: + * - namespace: Namespace this directive aliases to + * - name: Directive name this directive aliases to + * + * In certain degenerate cases, stdClass will actually be an integer. In + * that case, the value is equivalent to an stdClass with the type + * property set to the integer. If the integer is negative, type is + * equal to the absolute value of integer, and allow_null is true. + * + * This class is friendly with HTMLPurifier_Config. If you need introspection + * about the schema, you're better of using the ConfigSchema_Interchange, + * which uses more memory but has much richer information. + * @type array + */ + public $info = array(); + + /** + * Application-wide singleton + * @type HTMLPurifier_ConfigSchema + */ + protected static $singleton; + + public function __construct() + { + $this->defaultPlist = new HTMLPurifier_PropertyList(); + } + + /** + * Unserializes the default ConfigSchema. + * @return HTMLPurifier_ConfigSchema + */ + public static function makeFromSerial() + { + $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'); + $r = unserialize($contents); + if (!$r) { + $hash = sha1($contents); + trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR); + } + return $r; + } + + /** + * Retrieves an instance of the application-wide configuration definition. + * @param HTMLPurifier_ConfigSchema $prototype + * @return HTMLPurifier_ConfigSchema + */ + public static function instance($prototype = null) + { + if ($prototype !== null) { + HTMLPurifier_ConfigSchema::$singleton = $prototype; + } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) { + HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial(); + } + return HTMLPurifier_ConfigSchema::$singleton; + } + + /** + * Defines a directive for configuration + * @warning Will fail of directive's namespace is defined. + * @warning This method's signature is slightly different from the legacy + * define() static method! Beware! + * @param string $key Name of directive + * @param mixed $default Default value of directive + * @param string $type Allowed type of the directive. See + * HTMLPurifier_VarParser::$types for allowed values + * @param bool $allow_null Whether or not to allow null values + */ + public function add($key, $default, $type, $allow_null) + { + $obj = new stdClass(); + $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type]; + if ($allow_null) { + $obj->allow_null = true; + } + $this->info[$key] = $obj; + $this->defaults[$key] = $default; + $this->defaultPlist->set($key, $default); + } + + /** + * Defines a directive value alias. + * + * Directive value aliases are convenient for developers because it lets + * them set a directive to several values and get the same result. + * @param string $key Name of Directive + * @param array $aliases Hash of aliased values to the real alias + */ + public function addValueAliases($key, $aliases) + { + if (!isset($this->info[$key]->aliases)) { + $this->info[$key]->aliases = array(); + } + foreach ($aliases as $alias => $real) { + $this->info[$key]->aliases[$alias] = $real; + } + } + + /** + * Defines a set of allowed values for a directive. + * @warning This is slightly different from the corresponding static + * method definition. + * @param string $key Name of directive + * @param array $allowed Lookup array of allowed values + */ + public function addAllowedValues($key, $allowed) + { + $this->info[$key]->allowed = $allowed; + } + + /** + * Defines a directive alias for backwards compatibility + * @param string $key Directive that will be aliased + * @param string $new_key Directive that the alias will be to + */ + public function addAlias($key, $new_key) + { + $obj = new stdClass; + $obj->key = $new_key; + $obj->isAlias = true; + $this->info[$key] = $obj; + } + + /** + * Replaces any stdClass that only has the type property with type integer. + */ + public function postProcess() + { + foreach ($this->info as $key => $v) { + if (count((array) $v) == 1) { + $this->info[$key] = $v->type; + } elseif (count((array) $v) == 2 && isset($v->allow_null)) { + $this->info[$key] = -$v->type; + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php new file mode 100644 index 00000000000..d5906cd46dd --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php @@ -0,0 +1,48 @@ +directives as $d) { + $schema->add( + $d->id->key, + $d->default, + $d->type, + $d->typeAllowsNull + ); + if ($d->allowed !== null) { + $schema->addAllowedValues( + $d->id->key, + $d->allowed + ); + } + foreach ($d->aliases as $alias) { + $schema->addAlias( + $alias->key, + $d->id->key + ); + } + if ($d->valueAliases !== null) { + $schema->addValueAliases( + $d->id->key, + $d->valueAliases + ); + } + } + $schema->postProcess(); + return $schema; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php new file mode 100644 index 00000000000..5fa56f7ddb6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php @@ -0,0 +1,144 @@ +startElement('div'); + + $purifier = HTMLPurifier::getInstance(); + $html = $purifier->purify($html); + $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); + $this->writeRaw($html); + + $this->endElement(); // div + } + + /** + * @param mixed $var + * @return string + */ + protected function export($var) + { + if ($var === array()) { + return 'array()'; + } + return var_export($var, true); + } + + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + */ + public function build($interchange) + { + // global access, only use as last resort + $this->interchange = $interchange; + + $this->setIndent(true); + $this->startDocument('1.0', 'UTF-8'); + $this->startElement('configdoc'); + $this->writeElement('title', $interchange->name); + + foreach ($interchange->directives as $directive) { + $this->buildDirective($directive); + } + + if ($this->namespace) { + $this->endElement(); + } // namespace + + $this->endElement(); // configdoc + $this->flush(); + } + + /** + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive + */ + public function buildDirective($directive) + { + // Kludge, although I suppose having a notion of a "root namespace" + // certainly makes things look nicer when documentation is built. + // Depends on things being sorted. + if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) { + if ($this->namespace) { + $this->endElement(); + } // namespace + $this->namespace = $directive->id->getRootNamespace(); + $this->startElement('namespace'); + $this->writeAttribute('id', $this->namespace); + $this->writeElement('name', $this->namespace); + } + + $this->startElement('directive'); + $this->writeAttribute('id', $directive->id->toString()); + + $this->writeElement('name', $directive->id->getDirective()); + + $this->startElement('aliases'); + foreach ($directive->aliases as $alias) { + $this->writeElement('alias', $alias->toString()); + } + $this->endElement(); // aliases + + $this->startElement('constraints'); + if ($directive->version) { + $this->writeElement('version', $directive->version); + } + $this->startElement('type'); + if ($directive->typeAllowsNull) { + $this->writeAttribute('allow-null', 'yes'); + } + $this->text($directive->type); + $this->endElement(); // type + if ($directive->allowed) { + $this->startElement('allowed'); + foreach ($directive->allowed as $value => $x) { + $this->writeElement('value', $value); + } + $this->endElement(); // allowed + } + $this->writeElement('default', $this->export($directive->default)); + $this->writeAttribute('xml:space', 'preserve'); + if ($directive->external) { + $this->startElement('external'); + foreach ($directive->external as $project) { + $this->writeElement('project', $project); + } + $this->endElement(); + } + $this->endElement(); // constraints + + if ($directive->deprecatedVersion) { + $this->startElement('deprecated'); + $this->writeElement('version', $directive->deprecatedVersion); + $this->writeElement('use', $directive->deprecatedUse->toString()); + $this->endElement(); // deprecated + } + + $this->startElement('description'); + $this->writeHTMLDiv($directive->description); + $this->endElement(); // description + + $this->endElement(); // directive + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php new file mode 100644 index 00000000000..2671516c588 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php @@ -0,0 +1,11 @@ + array(directive info) + * @type HTMLPurifier_ConfigSchema_Interchange_Directive[] + */ + public $directives = array(); + + /** + * Adds a directive array to $directives + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive + * @throws HTMLPurifier_ConfigSchema_Exception + */ + public function addDirective($directive) + { + if (isset($this->directives[$i = $directive->id->toString()])) { + throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'"); + } + $this->directives[$i] = $directive; + } + + /** + * Convenience function to perform standard validation. Throws exception + * on failed validation. + */ + public function validate() + { + $validator = new HTMLPurifier_ConfigSchema_Validator(); + return $validator->validate($this); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php new file mode 100644 index 00000000000..127a39a673e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php @@ -0,0 +1,89 @@ + true). + * Null if all values are allowed. + * @type array + */ + public $allowed; + + /** + * List of aliases for the directive. + * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))). + * @type HTMLPurifier_ConfigSchema_Interchange_Id[] + */ + public $aliases = array(); + + /** + * Hash of value aliases, e.g. array('alt' => 'real'). Null if value + * aliasing is disabled (necessary for non-scalar types). + * @type array + */ + public $valueAliases; + + /** + * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'. + * Null if the directive has always existed. + * @type string + */ + public $version; + + /** + * ID of directive that supercedes this old directive. + * Null if not deprecated. + * @type HTMLPurifier_ConfigSchema_Interchange_Id + */ + public $deprecatedUse; + + /** + * Version of HTML Purifier this directive was deprecated. Null if not + * deprecated. + * @type string + */ + public $deprecatedVersion; + + /** + * List of external projects this directive depends on, e.g. array('CSSTidy'). + * @type array + */ + public $external = array(); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php new file mode 100644 index 00000000000..126f09d957c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php @@ -0,0 +1,58 @@ +key = $key; + } + + /** + * @return string + * @warning This is NOT magic, to ensure that people don't abuse SPL and + * cause problems for PHP 5.0 support. + */ + public function toString() + { + return $this->key; + } + + /** + * @return string + */ + public function getRootNamespace() + { + return substr($this->key, 0, strpos($this->key, ".")); + } + + /** + * @return string + */ + public function getDirective() + { + return substr($this->key, strpos($this->key, ".") + 1); + } + + /** + * @param string $id + * @return HTMLPurifier_ConfigSchema_Interchange_Id + */ + public static function make($id) + { + return new HTMLPurifier_ConfigSchema_Interchange_Id($id); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php new file mode 100644 index 00000000000..655e6dd1b92 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php @@ -0,0 +1,226 @@ +varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native(); + } + + /** + * @param string $dir + * @return HTMLPurifier_ConfigSchema_Interchange + */ + public static function buildFromDirectory($dir = null) + { + $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); + $interchange = new HTMLPurifier_ConfigSchema_Interchange(); + return $builder->buildDir($interchange, $dir); + } + + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param string $dir + * @return HTMLPurifier_ConfigSchema_Interchange + */ + public function buildDir($interchange, $dir = null) + { + if (!$dir) { + $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema'; + } + if (file_exists($dir . '/info.ini')) { + $info = parse_ini_file($dir . '/info.ini'); + $interchange->name = $info['name']; + } + + $files = array(); + $dh = opendir($dir); + while (false !== ($file = readdir($dh))) { + if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') { + continue; + } + $files[] = $file; + } + closedir($dh); + + sort($files); + foreach ($files as $file) { + $this->buildFile($interchange, $dir . '/' . $file); + } + return $interchange; + } + + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param string $file + */ + public function buildFile($interchange, $file) + { + $parser = new HTMLPurifier_StringHashParser(); + $this->build( + $interchange, + new HTMLPurifier_StringHash($parser->parseFile($file)) + ); + } + + /** + * Builds an interchange object based on a hash. + * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build + * @param HTMLPurifier_StringHash $hash source data + * @throws HTMLPurifier_ConfigSchema_Exception + */ + public function build($interchange, $hash) + { + if (!$hash instanceof HTMLPurifier_StringHash) { + $hash = new HTMLPurifier_StringHash($hash); + } + if (!isset($hash['ID'])) { + throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID'); + } + if (strpos($hash['ID'], '.') === false) { + if (count($hash) == 2 && isset($hash['DESCRIPTION'])) { + $hash->offsetGet('DESCRIPTION'); // prevent complaining + } else { + throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace'); + } + } else { + $this->buildDirective($interchange, $hash); + } + $this->_findUnused($hash); + } + + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param HTMLPurifier_StringHash $hash + * @throws HTMLPurifier_ConfigSchema_Exception + */ + public function buildDirective($interchange, $hash) + { + $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive(); + + // These are required elements: + $directive->id = $this->id($hash->offsetGet('ID')); + $id = $directive->id->toString(); // convenience + + if (isset($hash['TYPE'])) { + $type = explode('/', $hash->offsetGet('TYPE')); + if (isset($type[1])) { + $directive->typeAllowsNull = true; + } + $directive->type = $type[0]; + } else { + throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined"); + } + + if (isset($hash['DEFAULT'])) { + try { + $directive->default = $this->varParser->parse( + $hash->offsetGet('DEFAULT'), + $directive->type, + $directive->typeAllowsNull + ); + } catch (HTMLPurifier_VarParserException $e) { + throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'"); + } + } + + if (isset($hash['DESCRIPTION'])) { + $directive->description = $hash->offsetGet('DESCRIPTION'); + } + + if (isset($hash['ALLOWED'])) { + $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED'))); + } + + if (isset($hash['VALUE-ALIASES'])) { + $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES')); + } + + if (isset($hash['ALIASES'])) { + $raw_aliases = trim($hash->offsetGet('ALIASES')); + $aliases = preg_split('/\s*,\s*/', $raw_aliases); + foreach ($aliases as $alias) { + $directive->aliases[] = $this->id($alias); + } + } + + if (isset($hash['VERSION'])) { + $directive->version = $hash->offsetGet('VERSION'); + } + + if (isset($hash['DEPRECATED-USE'])) { + $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE')); + } + + if (isset($hash['DEPRECATED-VERSION'])) { + $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION'); + } + + if (isset($hash['EXTERNAL'])) { + $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL'))); + } + + $interchange->addDirective($directive); + } + + /** + * Evaluates an array PHP code string without array() wrapper + * @param string $contents + */ + protected function evalArray($contents) + { + return eval('return array(' . $contents . ');'); + } + + /** + * Converts an array list into a lookup array. + * @param array $array + * @return array + */ + protected function lookup($array) + { + $ret = array(); + foreach ($array as $val) { + $ret[$val] = true; + } + return $ret; + } + + /** + * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id + * object based on a string Id. + * @param string $id + * @return HTMLPurifier_ConfigSchema_Interchange_Id + */ + protected function id($id) + { + return HTMLPurifier_ConfigSchema_Interchange_Id::make($id); + } + + /** + * Triggers errors for any unused keys passed in the hash; such keys + * may indicate typos, missing values, etc. + * @param HTMLPurifier_StringHash $hash Hash to check. + */ + protected function _findUnused($hash) + { + $accessed = $hash->getAccessed(); + foreach ($hash as $k => $v) { + if (!isset($accessed[$k])) { + trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE); + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php new file mode 100644 index 00000000000..fb31277889f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php @@ -0,0 +1,248 @@ +parser = new HTMLPurifier_VarParser(); + } + + /** + * Validates a fully-formed interchange object. + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @return bool + */ + public function validate($interchange) + { + $this->interchange = $interchange; + $this->aliases = array(); + // PHP is a bit lax with integer <=> string conversions in + // arrays, so we don't use the identical !== comparison + foreach ($interchange->directives as $i => $directive) { + $id = $directive->id->toString(); + if ($i != $id) { + $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); + } + $this->validateDirective($directive); + } + return true; + } + + /** + * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object. + * @param HTMLPurifier_ConfigSchema_Interchange_Id $id + */ + public function validateId($id) + { + $id_string = $id->toString(); + $this->context[] = "id '$id_string'"; + if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) { + // handled by InterchangeBuilder + $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id'); + } + // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.) + // we probably should check that it has at least one namespace + $this->with($id, 'key') + ->assertNotEmpty() + ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder + array_pop($this->context); + } + + /** + * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d + */ + public function validateDirective($d) + { + $id = $d->id->toString(); + $this->context[] = "directive '$id'"; + $this->validateId($d->id); + + $this->with($d, 'description') + ->assertNotEmpty(); + + // BEGIN - handled by InterchangeBuilder + $this->with($d, 'type') + ->assertNotEmpty(); + $this->with($d, 'typeAllowsNull') + ->assertIsBool(); + try { + // This also tests validity of $d->type + $this->parser->parse($d->default, $d->type, $d->typeAllowsNull); + } catch (HTMLPurifier_VarParserException $e) { + $this->error('default', 'had error: ' . $e->getMessage()); + } + // END - handled by InterchangeBuilder + + if (!is_null($d->allowed) || !empty($d->valueAliases)) { + // allowed and valueAliases require that we be dealing with + // strings, so check for that early. + $d_int = HTMLPurifier_VarParser::$types[$d->type]; + if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) { + $this->error('type', 'must be a string type when used with allowed or value aliases'); + } + } + + $this->validateDirectiveAllowed($d); + $this->validateDirectiveValueAliases($d); + $this->validateDirectiveAliases($d); + + array_pop($this->context); + } + + /** + * Extra validation if $allowed member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d + */ + public function validateDirectiveAllowed($d) + { + if (is_null($d->allowed)) { + return; + } + $this->with($d, 'allowed') + ->assertNotEmpty() + ->assertIsLookup(); // handled by InterchangeBuilder + if (is_string($d->default) && !isset($d->allowed[$d->default])) { + $this->error('default', 'must be an allowed value'); + } + $this->context[] = 'allowed'; + foreach ($d->allowed as $val => $x) { + if (!is_string($val)) { + $this->error("value $val", 'must be a string'); + } + } + array_pop($this->context); + } + + /** + * Extra validation if $valueAliases member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d + */ + public function validateDirectiveValueAliases($d) + { + if (is_null($d->valueAliases)) { + return; + } + $this->with($d, 'valueAliases') + ->assertIsArray(); // handled by InterchangeBuilder + $this->context[] = 'valueAliases'; + foreach ($d->valueAliases as $alias => $real) { + if (!is_string($alias)) { + $this->error("alias $alias", 'must be a string'); + } + if (!is_string($real)) { + $this->error("alias target $real from alias '$alias'", 'must be a string'); + } + if ($alias === $real) { + $this->error("alias '$alias'", "must not be an alias to itself"); + } + } + if (!is_null($d->allowed)) { + foreach ($d->valueAliases as $alias => $real) { + if (isset($d->allowed[$alias])) { + $this->error("alias '$alias'", 'must not be an allowed value'); + } elseif (!isset($d->allowed[$real])) { + $this->error("alias '$alias'", 'must be an alias to an allowed value'); + } + } + } + array_pop($this->context); + } + + /** + * Extra validation if $aliases member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d + */ + public function validateDirectiveAliases($d) + { + $this->with($d, 'aliases') + ->assertIsArray(); // handled by InterchangeBuilder + $this->context[] = 'aliases'; + foreach ($d->aliases as $alias) { + $this->validateId($alias); + $s = $alias->toString(); + if (isset($this->interchange->directives[$s])) { + $this->error("alias '$s'", 'collides with another directive'); + } + if (isset($this->aliases[$s])) { + $other_directive = $this->aliases[$s]; + $this->error("alias '$s'", "collides with alias for directive '$other_directive'"); + } + $this->aliases[$s] = $d->id->toString(); + } + array_pop($this->context); + } + + // protected helper functions + + /** + * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom + * for validating simple member variables of objects. + * @param $obj + * @param $member + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + protected function with($obj, $member) + { + return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member); + } + + /** + * Emits an error, providing helpful context. + * @throws HTMLPurifier_ConfigSchema_Exception + */ + protected function error($target, $msg) + { + if ($target !== false) { + $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext(); + } else { + $prefix = ucfirst($this->getFormattedContext()); + } + throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg)); + } + + /** + * Returns a formatted context string. + * @return string + */ + protected function getFormattedContext() + { + return implode(' in ', array_reverse($this->context)); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php new file mode 100644 index 00000000000..c9aa3644af7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php @@ -0,0 +1,130 @@ +context = $context; + $this->obj = $obj; + $this->member = $member; + $this->contents =& $obj->$member; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsString() + { + if (!is_string($this->contents)) { + $this->error('must be a string'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsBool() + { + if (!is_bool($this->contents)) { + $this->error('must be a boolean'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsArray() + { + if (!is_array($this->contents)) { + $this->error('must be an array'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertNotNull() + { + if ($this->contents === null) { + $this->error('must not be null'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertAlnum() + { + $this->assertIsString(); + if (!ctype_alnum($this->contents)) { + $this->error('must be alphanumeric'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertNotEmpty() + { + if (empty($this->contents)) { + $this->error('must not be empty'); + } + return $this; + } + + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsLookup() + { + $this->assertIsArray(); + foreach ($this->contents as $v) { + if ($v !== true) { + $this->error('must be a lookup array'); + } + } + return $this; + } + + /** + * @param string $msg + * @throws HTMLPurifier_ConfigSchema_Exception + */ + protected function error($msg) + { + throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser new file mode 100644 index 00000000000..a5426c73665 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser @@ -0,0 +1 @@ +O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:127:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:23:"Core.AllowParseManyTags";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.ColorKeywords";a:148:{s:9:"aliceblue";s:7:"#F0F8FF";s:12:"antiquewhite";s:7:"#FAEBD7";s:4:"aqua";s:7:"#00FFFF";s:10:"aquamarine";s:7:"#7FFFD4";s:5:"azure";s:7:"#F0FFFF";s:5:"beige";s:7:"#F5F5DC";s:6:"bisque";s:7:"#FFE4C4";s:5:"black";s:7:"#000000";s:14:"blanchedalmond";s:7:"#FFEBCD";s:4:"blue";s:7:"#0000FF";s:10:"blueviolet";s:7:"#8A2BE2";s:5:"brown";s:7:"#A52A2A";s:9:"burlywood";s:7:"#DEB887";s:9:"cadetblue";s:7:"#5F9EA0";s:10:"chartreuse";s:7:"#7FFF00";s:9:"chocolate";s:7:"#D2691E";s:5:"coral";s:7:"#FF7F50";s:14:"cornflowerblue";s:7:"#6495ED";s:8:"cornsilk";s:7:"#FFF8DC";s:7:"crimson";s:7:"#DC143C";s:4:"cyan";s:7:"#00FFFF";s:8:"darkblue";s:7:"#00008B";s:8:"darkcyan";s:7:"#008B8B";s:13:"darkgoldenrod";s:7:"#B8860B";s:8:"darkgray";s:7:"#A9A9A9";s:8:"darkgrey";s:7:"#A9A9A9";s:9:"darkgreen";s:7:"#006400";s:9:"darkkhaki";s:7:"#BDB76B";s:11:"darkmagenta";s:7:"#8B008B";s:14:"darkolivegreen";s:7:"#556B2F";s:10:"darkorange";s:7:"#FF8C00";s:10:"darkorchid";s:7:"#9932CC";s:7:"darkred";s:7:"#8B0000";s:10:"darksalmon";s:7:"#E9967A";s:12:"darkseagreen";s:7:"#8FBC8F";s:13:"darkslateblue";s:7:"#483D8B";s:13:"darkslategray";s:7:"#2F4F4F";s:13:"darkslategrey";s:7:"#2F4F4F";s:13:"darkturquoise";s:7:"#00CED1";s:10:"darkviolet";s:7:"#9400D3";s:8:"deeppink";s:7:"#FF1493";s:11:"deepskyblue";s:7:"#00BFFF";s:7:"dimgray";s:7:"#696969";s:7:"dimgrey";s:7:"#696969";s:10:"dodgerblue";s:7:"#1E90FF";s:9:"firebrick";s:7:"#B22222";s:11:"floralwhite";s:7:"#FFFAF0";s:11:"forestgreen";s:7:"#228B22";s:7:"fuchsia";s:7:"#FF00FF";s:9:"gainsboro";s:7:"#DCDCDC";s:10:"ghostwhite";s:7:"#F8F8FF";s:4:"gold";s:7:"#FFD700";s:9:"goldenrod";s:7:"#DAA520";s:4:"gray";s:7:"#808080";s:4:"grey";s:7:"#808080";s:5:"green";s:7:"#008000";s:11:"greenyellow";s:7:"#ADFF2F";s:8:"honeydew";s:7:"#F0FFF0";s:7:"hotpink";s:7:"#FF69B4";s:9:"indianred";s:7:"#CD5C5C";s:6:"indigo";s:7:"#4B0082";s:5:"ivory";s:7:"#FFFFF0";s:5:"khaki";s:7:"#F0E68C";s:8:"lavender";s:7:"#E6E6FA";s:13:"lavenderblush";s:7:"#FFF0F5";s:9:"lawngreen";s:7:"#7CFC00";s:12:"lemonchiffon";s:7:"#FFFACD";s:9:"lightblue";s:7:"#ADD8E6";s:10:"lightcoral";s:7:"#F08080";s:9:"lightcyan";s:7:"#E0FFFF";s:20:"lightgoldenrodyellow";s:7:"#FAFAD2";s:9:"lightgray";s:7:"#D3D3D3";s:9:"lightgrey";s:7:"#D3D3D3";s:10:"lightgreen";s:7:"#90EE90";s:9:"lightpink";s:7:"#FFB6C1";s:11:"lightsalmon";s:7:"#FFA07A";s:13:"lightseagreen";s:7:"#20B2AA";s:12:"lightskyblue";s:7:"#87CEFA";s:14:"lightslategray";s:7:"#778899";s:14:"lightslategrey";s:7:"#778899";s:14:"lightsteelblue";s:7:"#B0C4DE";s:11:"lightyellow";s:7:"#FFFFE0";s:4:"lime";s:7:"#00FF00";s:9:"limegreen";s:7:"#32CD32";s:5:"linen";s:7:"#FAF0E6";s:7:"magenta";s:7:"#FF00FF";s:6:"maroon";s:7:"#800000";s:16:"mediumaquamarine";s:7:"#66CDAA";s:10:"mediumblue";s:7:"#0000CD";s:12:"mediumorchid";s:7:"#BA55D3";s:12:"mediumpurple";s:7:"#9370DB";s:14:"mediumseagreen";s:7:"#3CB371";s:15:"mediumslateblue";s:7:"#7B68EE";s:17:"mediumspringgreen";s:7:"#00FA9A";s:15:"mediumturquoise";s:7:"#48D1CC";s:15:"mediumvioletred";s:7:"#C71585";s:12:"midnightblue";s:7:"#191970";s:9:"mintcream";s:7:"#F5FFFA";s:9:"mistyrose";s:7:"#FFE4E1";s:8:"moccasin";s:7:"#FFE4B5";s:11:"navajowhite";s:7:"#FFDEAD";s:4:"navy";s:7:"#000080";s:7:"oldlace";s:7:"#FDF5E6";s:5:"olive";s:7:"#808000";s:9:"olivedrab";s:7:"#6B8E23";s:6:"orange";s:7:"#FFA500";s:9:"orangered";s:7:"#FF4500";s:6:"orchid";s:7:"#DA70D6";s:13:"palegoldenrod";s:7:"#EEE8AA";s:9:"palegreen";s:7:"#98FB98";s:13:"paleturquoise";s:7:"#AFEEEE";s:13:"palevioletred";s:7:"#DB7093";s:10:"papayawhip";s:7:"#FFEFD5";s:9:"peachpuff";s:7:"#FFDAB9";s:4:"peru";s:7:"#CD853F";s:4:"pink";s:7:"#FFC0CB";s:4:"plum";s:7:"#DDA0DD";s:10:"powderblue";s:7:"#B0E0E6";s:6:"purple";s:7:"#800080";s:13:"rebeccapurple";s:7:"#663399";s:3:"red";s:7:"#FF0000";s:9:"rosybrown";s:7:"#BC8F8F";s:9:"royalblue";s:7:"#4169E1";s:11:"saddlebrown";s:7:"#8B4513";s:6:"salmon";s:7:"#FA8072";s:10:"sandybrown";s:7:"#F4A460";s:8:"seagreen";s:7:"#2E8B57";s:8:"seashell";s:7:"#FFF5EE";s:6:"sienna";s:7:"#A0522D";s:6:"silver";s:7:"#C0C0C0";s:7:"skyblue";s:7:"#87CEEB";s:9:"slateblue";s:7:"#6A5ACD";s:9:"slategray";s:7:"#708090";s:9:"slategrey";s:7:"#708090";s:4:"snow";s:7:"#FFFAFA";s:11:"springgreen";s:7:"#00FF7F";s:9:"steelblue";s:7:"#4682B4";s:3:"tan";s:7:"#D2B48C";s:4:"teal";s:7:"#008080";s:7:"thistle";s:7:"#D8BFD8";s:6:"tomato";s:7:"#FF6347";s:9:"turquoise";s:7:"#40E0D0";s:6:"violet";s:7:"#EE82EE";s:5:"wheat";s:7:"#F5DEB3";s:5:"white";s:7:"#FFFFFF";s:10:"whitesmoke";s:7:"#F5F5F5";s:6:"yellow";s:7:"#FFFF00";s:11:"yellowgreen";s:7:"#9ACD32";}s:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:10:"HTML.Forms";b:0;s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;s:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:12:"defaultPlist";O:25:"HTMLPurifier_PropertyList":3:{s:7:"*data";a:127:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:23:"Core.AllowParseManyTags";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.ColorKeywords";a:148:{s:9:"aliceblue";s:7:"#F0F8FF";s:12:"antiquewhite";s:7:"#FAEBD7";s:4:"aqua";s:7:"#00FFFF";s:10:"aquamarine";s:7:"#7FFFD4";s:5:"azure";s:7:"#F0FFFF";s:5:"beige";s:7:"#F5F5DC";s:6:"bisque";s:7:"#FFE4C4";s:5:"black";s:7:"#000000";s:14:"blanchedalmond";s:7:"#FFEBCD";s:4:"blue";s:7:"#0000FF";s:10:"blueviolet";s:7:"#8A2BE2";s:5:"brown";s:7:"#A52A2A";s:9:"burlywood";s:7:"#DEB887";s:9:"cadetblue";s:7:"#5F9EA0";s:10:"chartreuse";s:7:"#7FFF00";s:9:"chocolate";s:7:"#D2691E";s:5:"coral";s:7:"#FF7F50";s:14:"cornflowerblue";s:7:"#6495ED";s:8:"cornsilk";s:7:"#FFF8DC";s:7:"crimson";s:7:"#DC143C";s:4:"cyan";s:7:"#00FFFF";s:8:"darkblue";s:7:"#00008B";s:8:"darkcyan";s:7:"#008B8B";s:13:"darkgoldenrod";s:7:"#B8860B";s:8:"darkgray";s:7:"#A9A9A9";s:8:"darkgrey";s:7:"#A9A9A9";s:9:"darkgreen";s:7:"#006400";s:9:"darkkhaki";s:7:"#BDB76B";s:11:"darkmagenta";s:7:"#8B008B";s:14:"darkolivegreen";s:7:"#556B2F";s:10:"darkorange";s:7:"#FF8C00";s:10:"darkorchid";s:7:"#9932CC";s:7:"darkred";s:7:"#8B0000";s:10:"darksalmon";s:7:"#E9967A";s:12:"darkseagreen";s:7:"#8FBC8F";s:13:"darkslateblue";s:7:"#483D8B";s:13:"darkslategray";s:7:"#2F4F4F";s:13:"darkslategrey";s:7:"#2F4F4F";s:13:"darkturquoise";s:7:"#00CED1";s:10:"darkviolet";s:7:"#9400D3";s:8:"deeppink";s:7:"#FF1493";s:11:"deepskyblue";s:7:"#00BFFF";s:7:"dimgray";s:7:"#696969";s:7:"dimgrey";s:7:"#696969";s:10:"dodgerblue";s:7:"#1E90FF";s:9:"firebrick";s:7:"#B22222";s:11:"floralwhite";s:7:"#FFFAF0";s:11:"forestgreen";s:7:"#228B22";s:7:"fuchsia";s:7:"#FF00FF";s:9:"gainsboro";s:7:"#DCDCDC";s:10:"ghostwhite";s:7:"#F8F8FF";s:4:"gold";s:7:"#FFD700";s:9:"goldenrod";s:7:"#DAA520";s:4:"gray";s:7:"#808080";s:4:"grey";s:7:"#808080";s:5:"green";s:7:"#008000";s:11:"greenyellow";s:7:"#ADFF2F";s:8:"honeydew";s:7:"#F0FFF0";s:7:"hotpink";s:7:"#FF69B4";s:9:"indianred";s:7:"#CD5C5C";s:6:"indigo";s:7:"#4B0082";s:5:"ivory";s:7:"#FFFFF0";s:5:"khaki";s:7:"#F0E68C";s:8:"lavender";s:7:"#E6E6FA";s:13:"lavenderblush";s:7:"#FFF0F5";s:9:"lawngreen";s:7:"#7CFC00";s:12:"lemonchiffon";s:7:"#FFFACD";s:9:"lightblue";s:7:"#ADD8E6";s:10:"lightcoral";s:7:"#F08080";s:9:"lightcyan";s:7:"#E0FFFF";s:20:"lightgoldenrodyellow";s:7:"#FAFAD2";s:9:"lightgray";s:7:"#D3D3D3";s:9:"lightgrey";s:7:"#D3D3D3";s:10:"lightgreen";s:7:"#90EE90";s:9:"lightpink";s:7:"#FFB6C1";s:11:"lightsalmon";s:7:"#FFA07A";s:13:"lightseagreen";s:7:"#20B2AA";s:12:"lightskyblue";s:7:"#87CEFA";s:14:"lightslategray";s:7:"#778899";s:14:"lightslategrey";s:7:"#778899";s:14:"lightsteelblue";s:7:"#B0C4DE";s:11:"lightyellow";s:7:"#FFFFE0";s:4:"lime";s:7:"#00FF00";s:9:"limegreen";s:7:"#32CD32";s:5:"linen";s:7:"#FAF0E6";s:7:"magenta";s:7:"#FF00FF";s:6:"maroon";s:7:"#800000";s:16:"mediumaquamarine";s:7:"#66CDAA";s:10:"mediumblue";s:7:"#0000CD";s:12:"mediumorchid";s:7:"#BA55D3";s:12:"mediumpurple";s:7:"#9370DB";s:14:"mediumseagreen";s:7:"#3CB371";s:15:"mediumslateblue";s:7:"#7B68EE";s:17:"mediumspringgreen";s:7:"#00FA9A";s:15:"mediumturquoise";s:7:"#48D1CC";s:15:"mediumvioletred";s:7:"#C71585";s:12:"midnightblue";s:7:"#191970";s:9:"mintcream";s:7:"#F5FFFA";s:9:"mistyrose";s:7:"#FFE4E1";s:8:"moccasin";s:7:"#FFE4B5";s:11:"navajowhite";s:7:"#FFDEAD";s:4:"navy";s:7:"#000080";s:7:"oldlace";s:7:"#FDF5E6";s:5:"olive";s:7:"#808000";s:9:"olivedrab";s:7:"#6B8E23";s:6:"orange";s:7:"#FFA500";s:9:"orangered";s:7:"#FF4500";s:6:"orchid";s:7:"#DA70D6";s:13:"palegoldenrod";s:7:"#EEE8AA";s:9:"palegreen";s:7:"#98FB98";s:13:"paleturquoise";s:7:"#AFEEEE";s:13:"palevioletred";s:7:"#DB7093";s:10:"papayawhip";s:7:"#FFEFD5";s:9:"peachpuff";s:7:"#FFDAB9";s:4:"peru";s:7:"#CD853F";s:4:"pink";s:7:"#FFC0CB";s:4:"plum";s:7:"#DDA0DD";s:10:"powderblue";s:7:"#B0E0E6";s:6:"purple";s:7:"#800080";s:13:"rebeccapurple";s:7:"#663399";s:3:"red";s:7:"#FF0000";s:9:"rosybrown";s:7:"#BC8F8F";s:9:"royalblue";s:7:"#4169E1";s:11:"saddlebrown";s:7:"#8B4513";s:6:"salmon";s:7:"#FA8072";s:10:"sandybrown";s:7:"#F4A460";s:8:"seagreen";s:7:"#2E8B57";s:8:"seashell";s:7:"#FFF5EE";s:6:"sienna";s:7:"#A0522D";s:6:"silver";s:7:"#C0C0C0";s:7:"skyblue";s:7:"#87CEEB";s:9:"slateblue";s:7:"#6A5ACD";s:9:"slategray";s:7:"#708090";s:9:"slategrey";s:7:"#708090";s:4:"snow";s:7:"#FFFAFA";s:11:"springgreen";s:7:"#00FF7F";s:9:"steelblue";s:7:"#4682B4";s:3:"tan";s:7:"#D2B48C";s:4:"teal";s:7:"#008080";s:7:"thistle";s:7:"#D8BFD8";s:6:"tomato";s:7:"#FF6347";s:9:"turquoise";s:7:"#40E0D0";s:6:"violet";s:7:"#EE82EE";s:5:"wheat";s:7:"#F5DEB3";s:5:"white";s:7:"#FFFFFF";s:10:"whitesmoke";s:7:"#F5F5F5";s:6:"yellow";s:7:"#FFFF00";s:11:"yellowgreen";s:7:"#9ACD32";}s:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:10:"HTML.Forms";b:0;s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;s:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:9:"*parent";N;s:8:"*cache";N;}s:4:"info";a:140:{s:19:"Attr.AllowedClasses";i:-8;s:24:"Attr.AllowedFrameTargets";i:8;s:15:"Attr.AllowedRel";i:8;s:15:"Attr.AllowedRev";i:8;s:18:"Attr.ClassUseCDATA";i:-7;s:20:"Attr.DefaultImageAlt";i:-1;s:24:"Attr.DefaultInvalidImage";i:1;s:27:"Attr.DefaultInvalidImageAlt";i:1;s:19:"Attr.DefaultTextDir";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}}s:13:"Attr.EnableID";i:7;s:17:"HTML.EnableAttrID";O:8:"stdClass":2:{s:3:"key";s:13:"Attr.EnableID";s:7:"isAlias";b:1;}s:21:"Attr.ForbiddenClasses";i:8;s:13:"Attr.ID.HTML5";i:-7;s:16:"Attr.IDBlacklist";i:9;s:22:"Attr.IDBlacklistRegexp";i:-1;s:13:"Attr.IDPrefix";i:1;s:18:"Attr.IDPrefixLocal";i:1;s:24:"AutoFormat.AutoParagraph";i:7;s:17:"AutoFormat.Custom";i:9;s:25:"AutoFormat.DisplayLinkURI";i:7;s:18:"AutoFormat.Linkify";i:7;s:33:"AutoFormat.PurifierLinkify.DocURL";i:1;s:37:"AutoFormatParam.PurifierLinkifyDocURL";O:8:"stdClass":2:{s:3:"key";s:33:"AutoFormat.PurifierLinkify.DocURL";s:7:"isAlias";b:1;}s:26:"AutoFormat.PurifierLinkify";i:7;s:32:"AutoFormat.RemoveEmpty.Predicate";i:10;s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";i:8;s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";i:7;s:22:"AutoFormat.RemoveEmpty";i:7;s:39:"AutoFormat.RemoveSpansWithoutAttributes";i:7;s:19:"CSS.AllowDuplicates";i:7;s:18:"CSS.AllowImportant";i:7;s:15:"CSS.AllowTricky";i:7;s:16:"CSS.AllowedFonts";i:-8;s:21:"CSS.AllowedProperties";i:-8;s:17:"CSS.DefinitionRev";i:5;s:23:"CSS.ForbiddenProperties";i:8;s:16:"CSS.MaxImgLength";i:-1;s:15:"CSS.Proprietary";i:7;s:11:"CSS.Trusted";i:7;s:20:"Cache.DefinitionImpl";i:-1;s:20:"Core.DefinitionCache";O:8:"stdClass":2:{s:3:"key";s:20:"Cache.DefinitionImpl";s:7:"isAlias";b:1;}s:20:"Cache.SerializerPath";i:-1;s:27:"Cache.SerializerPermissions";i:-5;s:22:"Core.AggressivelyFixLt";i:7;s:29:"Core.AggressivelyRemoveScript";i:7;s:28:"Core.AllowHostnameUnderscore";i:7;s:23:"Core.AllowParseManyTags";i:7;s:18:"Core.CollectErrors";i:7;s:18:"Core.ColorKeywords";i:10;s:30:"Core.ConvertDocumentToFragment";i:7;s:24:"Core.AcceptFullDocuments";O:8:"stdClass":2:{s:3:"key";s:30:"Core.ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:36:"Core.DirectLexLineNumberSyncInterval";i:5;s:20:"Core.DisableExcludes";i:7;s:15:"Core.EnableIDNA";i:7;s:13:"Core.Encoding";i:2;s:26:"Core.EscapeInvalidChildren";i:7;s:22:"Core.EscapeInvalidTags";i:7;s:29:"Core.EscapeNonASCIICharacters";i:7;s:19:"Core.HiddenElements";i:8;s:13:"Core.Language";i:1;s:24:"Core.LegacyEntityDecoder";i:7;s:14:"Core.LexerImpl";i:-11;s:24:"Core.MaintainLineNumbers";i:-7;s:22:"Core.NormalizeNewlines";i:7;s:21:"Core.RemoveInvalidImg";i:7;s:33:"Core.RemoveProcessingInstructions";i:7;s:25:"Core.RemoveScriptContents";i:-7;s:13:"Filter.Custom";i:9;s:34:"Filter.ExtractStyleBlocks.Escaping";i:7;s:33:"Filter.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:38:"FilterParam.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:31:"Filter.ExtractStyleBlocks.Scope";i:-1;s:30:"Filter.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:35:"FilterParam.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:34:"Filter.ExtractStyleBlocks.TidyImpl";i:-11;s:38:"FilterParam.ExtractStyleBlocksTidyImpl";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.TidyImpl";s:7:"isAlias";b:1;}s:25:"Filter.ExtractStyleBlocks";i:7;s:14:"Filter.YouTube";i:7;s:12:"HTML.Allowed";i:-4;s:22:"HTML.AllowedAttributes";i:-8;s:20:"HTML.AllowedComments";i:8;s:26:"HTML.AllowedCommentsRegexp";i:-1;s:20:"HTML.AllowedElements";i:-8;s:19:"HTML.AllowedModules";i:-8;s:23:"HTML.Attr.Name.UseCDATA";i:7;s:17:"HTML.BlockWrapper";i:1;s:16:"HTML.CoreModules";i:8;s:18:"HTML.CustomDoctype";i:-1;s:17:"HTML.DefinitionID";i:-1;s:18:"HTML.DefinitionRev";i:5;s:12:"HTML.Doctype";O:8:"stdClass":3:{s:4:"type";i:1;s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}}s:25:"HTML.FlashAllowFullScreen";i:7;s:24:"HTML.ForbiddenAttributes";i:8;s:22:"HTML.ForbiddenElements";i:8;s:10:"HTML.Forms";i:7;s:17:"HTML.MaxImgLength";i:-5;s:13:"HTML.Nofollow";i:7;s:11:"HTML.Parent";i:1;s:16:"HTML.Proprietary";i:7;s:14:"HTML.SafeEmbed";i:7;s:15:"HTML.SafeIframe";i:7;s:15:"HTML.SafeObject";i:7;s:18:"HTML.SafeScripting";i:8;s:11:"HTML.Strict";i:7;s:16:"HTML.TargetBlank";i:7;s:19:"HTML.TargetNoopener";i:7;s:21:"HTML.TargetNoreferrer";i:7;s:12:"HTML.TidyAdd";i:8;s:14:"HTML.TidyLevel";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}}s:15:"HTML.TidyRemove";i:8;s:12:"HTML.Trusted";i:7;s:10:"HTML.XHTML";i:7;s:10:"Core.XHTML";O:8:"stdClass":2:{s:3:"key";s:10:"HTML.XHTML";s:7:"isAlias";b:1;}s:28:"Output.CommentScriptContents";i:7;s:26:"Core.CommentScriptContents";O:8:"stdClass":2:{s:3:"key";s:28:"Output.CommentScriptContents";s:7:"isAlias";b:1;}s:19:"Output.FixInnerHTML";i:7;s:18:"Output.FlashCompat";i:7;s:14:"Output.Newline";i:-1;s:15:"Output.SortAttr";i:7;s:17:"Output.TidyFormat";i:7;s:15:"Core.TidyFormat";O:8:"stdClass":2:{s:3:"key";s:17:"Output.TidyFormat";s:7:"isAlias";b:1;}s:17:"Test.ForceNoIconv";i:7;s:18:"URI.AllowedSchemes";i:8;s:8:"URI.Base";i:-1;s:17:"URI.DefaultScheme";i:-1;s:16:"URI.DefinitionID";i:-1;s:17:"URI.DefinitionRev";i:5;s:11:"URI.Disable";i:7;s:15:"Attr.DisableURI";O:8:"stdClass":2:{s:3:"key";s:11:"URI.Disable";s:7:"isAlias";b:1;}s:19:"URI.DisableExternal";i:7;s:28:"URI.DisableExternalResources";i:7;s:20:"URI.DisableResources";i:7;s:8:"URI.Host";i:-1;s:17:"URI.HostBlacklist";i:9;s:16:"URI.MakeAbsolute";i:7;s:9:"URI.Munge";i:-1;s:18:"URI.MungeResources";i:7;s:18:"URI.MungeSecretKey";i:-1;s:26:"URI.OverrideAllowedSchemes";i:7;s:20:"URI.SafeIframeRegexp";i:-1;}} \ No newline at end of file diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt new file mode 100644 index 00000000000..0517fed0a11 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt @@ -0,0 +1,8 @@ +Attr.AllowedClasses +TYPE: lookup/null +VERSION: 4.0.0 +DEFAULT: null +--DESCRIPTION-- +List of allowed class values in the class attribute. By default, this is null, +which means all classes are allowed. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt new file mode 100644 index 00000000000..249edd647b0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt @@ -0,0 +1,12 @@ +Attr.AllowedFrameTargets +TYPE: lookup +DEFAULT: array() +--DESCRIPTION-- +Lookup table of all allowed link frame targets. Some commonly used link +targets include _blank, _self, _parent and _top. Values should be +lowercase, as validation will be done in a case-sensitive manner despite +W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute +so this directive will have no effect in that doctype. XHTML 1.1 does not +enable the Target module by default, you will have to manually enable it +(see the module documentation for more details.) +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt new file mode 100644 index 00000000000..9a8fa6a2e20 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt @@ -0,0 +1,9 @@ +Attr.AllowedRel +TYPE: lookup +VERSION: 1.6.0 +DEFAULT: array() +--DESCRIPTION-- +List of allowed forward document relationships in the rel attribute. Common +values may be nofollow or print. By default, this is empty, meaning that no +document relationships are allowed. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt new file mode 100644 index 00000000000..b0178834859 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt @@ -0,0 +1,9 @@ +Attr.AllowedRev +TYPE: lookup +VERSION: 1.6.0 +DEFAULT: array() +--DESCRIPTION-- +List of allowed reverse document relationships in the rev attribute. This +attribute is a bit of an edge-case; if you don't know what it is for, stay +away. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt new file mode 100644 index 00000000000..e774b823b1b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt @@ -0,0 +1,19 @@ +Attr.ClassUseCDATA +TYPE: bool/null +DEFAULT: null +VERSION: 4.0.0 +--DESCRIPTION-- +If null, class will auto-detect the doctype and, if matching XHTML 1.1 or +XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise, +it will use a relaxed CDATA definition. If true, the relaxed CDATA definition +is forced; if false, the NMTOKENS definition is forced. To get behavior +of HTML Purifier prior to 4.0.0, set this directive to false. + +Some rational behind the auto-detection: +in previous versions of HTML Purifier, it was assumed that the form of +class was NMTOKENS, as specified by the XHTML Modularization (representing +XHTML 1.1 and XHTML 2.0). The DTDs for HTML 4.01 and XHTML 1.0, however +specify class as CDATA. HTML 5 effectively defines it as CDATA, but +with the additional constraint that each name should be unique (this is not +explicitly outlined in previous specifications). +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt new file mode 100644 index 00000000000..533165e1759 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt @@ -0,0 +1,11 @@ +Attr.DefaultImageAlt +TYPE: string/null +DEFAULT: null +VERSION: 3.2.0 +--DESCRIPTION-- +This is the content of the alt tag of an image if the user had not +previously specified an alt attribute. This applies to all images without +a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which +only applies to invalid images, and overrides in the case of an invalid image. +Default behavior with null is to use the basename of the src tag for the alt. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt new file mode 100644 index 00000000000..9eb7e38469f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt @@ -0,0 +1,9 @@ +Attr.DefaultInvalidImage +TYPE: string +DEFAULT: '' +--DESCRIPTION-- +This is the default image an img tag will be pointed to if it does not have +a valid src attribute. In future versions, we may allow the image tag to +be removed completely, but due to design issues, this is not possible right +now. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt new file mode 100644 index 00000000000..2f17bf477a3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt @@ -0,0 +1,8 @@ +Attr.DefaultInvalidImageAlt +TYPE: string +DEFAULT: 'Invalid image' +--DESCRIPTION-- +This is the content of the alt tag of an invalid image if the user had not +previously specified an alt attribute. It has no effect when the image is +valid but there was no alt attribute present. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt new file mode 100644 index 00000000000..52654b53ae3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt @@ -0,0 +1,10 @@ +Attr.DefaultTextDir +TYPE: string +DEFAULT: 'ltr' +--DESCRIPTION-- +Defines the default text direction (ltr or rtl) of the document being +parsed. This generally is the same as the value of the dir attribute in +HTML, or ltr if that is not specified. +--ALLOWED-- +'ltr', 'rtl' +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt new file mode 100644 index 00000000000..6440d210321 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt @@ -0,0 +1,16 @@ +Attr.EnableID +TYPE: bool +DEFAULT: false +VERSION: 1.2.0 +--DESCRIPTION-- +Allows the ID attribute in HTML. This is disabled by default due to the +fact that without proper configuration user input can easily break the +validation of a webpage by specifying an ID that is already on the +surrounding HTML. If you don't mind throwing caution to the wind, enable +this directive, but I strongly recommend you also consider blacklisting IDs +you use (%Attr.IDBlacklist) or prefixing all user supplied IDs +(%Attr.IDPrefix). When set to true HTML Purifier reverts to the behavior of +pre-1.2.0 versions. +--ALIASES-- +HTML.EnableAttrID +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt new file mode 100644 index 00000000000..f31d226f58b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt @@ -0,0 +1,8 @@ +Attr.ForbiddenClasses +TYPE: lookup +VERSION: 4.0.0 +DEFAULT: array() +--DESCRIPTION-- +List of forbidden class values in the class attribute. By default, this is +empty, which means that no classes are forbidden. See also %Attr.AllowedClasses. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt new file mode 100644 index 00000000000..735d4b7a10f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt @@ -0,0 +1,10 @@ +Attr.ID.HTML5 +TYPE: bool/null +DEFAULT: null +VERSION: 4.8.0 +--DESCRIPTION-- +In HTML5, restrictions on the format of the id attribute have been significantly +relaxed, such that any string is valid so long as it contains no spaces and +is at least one character. In lieu of a general HTML5 compatibility flag, +set this configuration directive to true to use the relaxed rules. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt new file mode 100644 index 00000000000..5f2b5e3d2cd --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt @@ -0,0 +1,5 @@ +Attr.IDBlacklist +TYPE: list +DEFAULT: array() +DESCRIPTION: Array of IDs not allowed in the document. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt new file mode 100644 index 00000000000..6f5824586ec --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt @@ -0,0 +1,9 @@ +Attr.IDBlacklistRegexp +TYPE: string/null +VERSION: 1.6.0 +DEFAULT: NULL +--DESCRIPTION-- +PCRE regular expression to be matched against all IDs. If the expression is +matches, the ID is rejected. Use this with care: may cause significant +degradation. ID matching is done after all other validation. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt new file mode 100644 index 00000000000..cc49d43fd0c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt @@ -0,0 +1,12 @@ +Attr.IDPrefix +TYPE: string +VERSION: 1.2.0 +DEFAULT: '' +--DESCRIPTION-- +String to prefix to IDs. If you have no idea what IDs your pages may use, +you may opt to simply add a prefix to all user-submitted ID attributes so +that they are still usable, but will not conflict with core page IDs. +Example: setting the directive to 'user_' will result in a user submitted +'foo' to become 'user_foo' Be sure to set %HTML.EnableAttrID to true +before using this. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt new file mode 100644 index 00000000000..2c5924a7ade --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt @@ -0,0 +1,14 @@ +Attr.IDPrefixLocal +TYPE: string +VERSION: 1.2.0 +DEFAULT: '' +--DESCRIPTION-- +Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If you +need to allow multiple sets of user content on web page, you may need to +have a seperate prefix that changes with each iteration. This way, +seperately submitted user content displayed on the same page doesn't +clobber each other. Ideal values are unique identifiers for the content it +represents (i.e. the id of the row in the database). Be sure to add a +seperator (like an underscore) at the end. Warning: this directive will +not work unless %Attr.IDPrefix is set to a non-empty value! +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt new file mode 100644 index 00000000000..d5caa1bb978 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt @@ -0,0 +1,31 @@ +AutoFormat.AutoParagraph +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +

+ This directive turns on auto-paragraphing, where double newlines are + converted in to paragraphs whenever possible. Auto-paragraphing: +

+
    +
  • Always applies to inline elements or text in the root node,
  • +
  • Applies to inline elements or text with double newlines in nodes + that allow paragraph tags,
  • +
  • Applies to double newlines in paragraph tags
  • +
+

+ p tags must be allowed for this directive to take effect. + We do not use br tags for paragraphing, as that is + semantically incorrect. +

+

+ To prevent auto-paragraphing as a content-producer, refrain from using + double-newlines except to specify a new paragraph or in contexts where + it has special meaning (whitespace usually has no meaning except in + tags like pre, so this should not be difficult.) To prevent + the paragraphing of inline text adjacent to block elements, wrap them + in div tags (the behavior is slightly different outside of + the root node.) +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt new file mode 100644 index 00000000000..2a476481af6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt @@ -0,0 +1,12 @@ +AutoFormat.Custom +TYPE: list +VERSION: 2.0.1 +DEFAULT: array() +--DESCRIPTION-- + +

+ This directive can be used to add custom auto-format injectors. + Specify an array of injector names (class name minus the prefix) + or concrete implementations. Injector class must exist. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt new file mode 100644 index 00000000000..663064a3447 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt @@ -0,0 +1,11 @@ +AutoFormat.DisplayLinkURI +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ This directive turns on the in-text display of URIs in <a> tags, and disables + those links. For example, example becomes + example (http://example.com). +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt new file mode 100644 index 00000000000..3a48ba960e9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt @@ -0,0 +1,12 @@ +AutoFormat.Linkify +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +

+ This directive turns on linkification, auto-linking http, ftp and + https URLs. a tags with the href attribute + must be allowed. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt new file mode 100644 index 00000000000..db58b134646 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt @@ -0,0 +1,12 @@ +AutoFormat.PurifierLinkify.DocURL +TYPE: string +VERSION: 2.0.1 +DEFAULT: '#%s' +ALIASES: AutoFormatParam.PurifierLinkifyDocURL +--DESCRIPTION-- +

+ Location of configuration documentation to link to, let %s substitute + into the configuration's namespace and directive names sans the percent + sign. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt new file mode 100644 index 00000000000..7996488be0d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt @@ -0,0 +1,12 @@ +AutoFormat.PurifierLinkify +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +

+ Internal auto-formatter that converts configuration directives in + syntax %Namespace.Directive to links. a tags + with the href attribute must be allowed. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt new file mode 100644 index 00000000000..6367fe23c99 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt @@ -0,0 +1,14 @@ +AutoFormat.RemoveEmpty.Predicate +TYPE: hash +VERSION: 4.7.0 +DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src')) +--DESCRIPTION-- +

+ Given that an element has no contents, it will be removed by default, unless + this predicate dictates otherwise. The predicate can either be an associative + map from tag name to list of attributes that must be present for the element + to be considered preserved: thus, the default always preserves colgroup, + th and td, and also iframe if it + has a src. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt new file mode 100644 index 00000000000..35c393b4e63 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt @@ -0,0 +1,11 @@ +AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions +TYPE: lookup +VERSION: 4.0.0 +DEFAULT: array('td' => true, 'th' => true) +--DESCRIPTION-- +

+ When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp + are enabled, this directive defines what HTML elements should not be + removede if they have only a non-breaking space in them. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt new file mode 100644 index 00000000000..9228dee228f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt @@ -0,0 +1,15 @@ +AutoFormat.RemoveEmpty.RemoveNbsp +TYPE: bool +VERSION: 4.0.0 +DEFAULT: false +--DESCRIPTION-- +

+ When enabled, HTML Purifier will treat any elements that contain only + non-breaking spaces as well as regular whitespace as empty, and remove + them when %AutoFormat.RemoveEmpty is enabled. +

+

+ See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements + that don't have this behavior applied to them. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt new file mode 100644 index 00000000000..34657ba47b1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt @@ -0,0 +1,46 @@ +AutoFormat.RemoveEmpty +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ When enabled, HTML Purifier will attempt to remove empty elements that + contribute no semantic information to the document. The following types + of nodes will be removed: +

+
  • + Tags with no attributes and no content, and that are not empty + elements (remove <a></a> but not + <br />), and +
  • +
  • + Tags with no content, except for:
      +
    • The colgroup element, or
    • +
    • + Elements with the id or name attribute, + when those attributes are permitted on those elements. +
    • +
  • +
+

+ Please be very careful when using this functionality; while it may not + seem that empty elements contain useful information, they can alter the + layout of a document given appropriate styling. This directive is most + useful when you are processing machine-generated HTML, please avoid using + it on regular user HTML. +

+

+ Elements that contain only whitespace will be treated as empty. Non-breaking + spaces, however, do not count as whitespace. See + %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior. +

+

+ This algorithm is not perfect; you may still notice some empty tags, + particularly if a node had elements, but those elements were later removed + because they were not permitted in that context, or tags that, after + being auto-closed by another tag, where empty. This is for safety reasons + to prevent clever code from breaking validation. The general rule of thumb: + if a tag looked empty on the way in, it will get removed; if HTML Purifier + made it empty, it will stay. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt new file mode 100644 index 00000000000..dde990ab260 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt @@ -0,0 +1,11 @@ +AutoFormat.RemoveSpansWithoutAttributes +TYPE: bool +VERSION: 4.0.1 +DEFAULT: false +--DESCRIPTION-- +

+ This directive causes span tags without any attributes + to be removed. It will also remove spans that had all attributes + removed during processing. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt new file mode 100644 index 00000000000..4d054b1f071 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt @@ -0,0 +1,11 @@ +CSS.AllowDuplicates +TYPE: bool +DEFAULT: false +VERSION: 4.8.0 +--DESCRIPTION-- +

+ By default, HTML Purifier removes duplicate CSS properties, + like color:red; color:blue. If this is set to + true, duplicate properties are allowed. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt new file mode 100644 index 00000000000..b324608f761 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt @@ -0,0 +1,8 @@ +CSS.AllowImportant +TYPE: bool +DEFAULT: false +VERSION: 3.1.0 +--DESCRIPTION-- +This parameter determines whether or not !important cascade modifiers should +be allowed in user CSS. If false, !important will stripped. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt new file mode 100644 index 00000000000..748be0eec8b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt @@ -0,0 +1,11 @@ +CSS.AllowTricky +TYPE: bool +DEFAULT: false +VERSION: 3.1.0 +--DESCRIPTION-- +This parameter determines whether or not to allow "tricky" CSS properties and +values. Tricky CSS properties/values can drastically modify page layout or +be used for deceptive practices but do not directly constitute a security risk. +For example, display:none; is considered a tricky property that +will only be allowed if this directive is set to true. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt new file mode 100644 index 00000000000..3fd4654065d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt @@ -0,0 +1,12 @@ +CSS.AllowedFonts +TYPE: lookup/null +VERSION: 4.3.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ Allows you to manually specify a set of allowed fonts. If + NULL, all fonts are allowed. This directive + affects generic names (serif, sans-serif, monospace, cursive, + fantasy) as well as specific font families. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt new file mode 100644 index 00000000000..460112ebe0a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt @@ -0,0 +1,18 @@ +CSS.AllowedProperties +TYPE: lookup/null +VERSION: 3.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ If HTML Purifier's style attributes set is unsatisfactory for your needs, + you can overload it with your own list of tags to allow. Note that this + method is subtractive: it does its job by taking away from HTML Purifier + usual feature set, so you cannot add an attribute that HTML Purifier never + supported in the first place. +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt new file mode 100644 index 00000000000..5cb7dda3bae --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt @@ -0,0 +1,11 @@ +CSS.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt new file mode 100644 index 00000000000..f1f5c5f12b4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt @@ -0,0 +1,13 @@ +CSS.ForbiddenProperties +TYPE: lookup +VERSION: 4.2.0 +DEFAULT: array() +--DESCRIPTION-- +

+ This is the logical inverse of %CSS.AllowedProperties, and it will + override that directive or any other directive. If possible, + %CSS.AllowedProperties is recommended over this directive, + because it can sometimes be difficult to tell whether or not you've + forbidden all of the CSS properties you truly would like to disallow. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt new file mode 100644 index 00000000000..7a3291470cc --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt @@ -0,0 +1,16 @@ +CSS.MaxImgLength +TYPE: string/null +DEFAULT: '1200px' +VERSION: 3.1.1 +--DESCRIPTION-- +

+ This parameter sets the maximum allowed length on img tags, + effectively the width and height properties. + Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is + in place to prevent imagecrash attacks, disable with null at your own risk. + This directive is similar to %HTML.MaxImgLength, and both should be + concurrently edited, although there are + subtle differences in the input format (the CSS max is a number with + a unit). +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt new file mode 100644 index 00000000000..148eedb8be2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt @@ -0,0 +1,10 @@ +CSS.Proprietary +TYPE: bool +VERSION: 3.0.0 +DEFAULT: false +--DESCRIPTION-- + +

+ Whether or not to allow safe, proprietary CSS values. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt new file mode 100644 index 00000000000..e733a61e8a7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt @@ -0,0 +1,9 @@ +CSS.Trusted +TYPE: bool +VERSION: 4.2.1 +DEFAULT: false +--DESCRIPTION-- +Indicates whether or not the user's CSS input is trusted or not. If the +input is trusted, a more expansive set of allowed properties. See +also %HTML.Trusted. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt new file mode 100644 index 00000000000..c486724c88a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt @@ -0,0 +1,14 @@ +Cache.DefinitionImpl +TYPE: string/null +VERSION: 2.0.0 +DEFAULT: 'Serializer' +--DESCRIPTION-- + +This directive defines which method to use when caching definitions, +the complex data-type that makes HTML Purifier tick. Set to null +to disable caching (not recommended, as you will see a definite +performance degradation). + +--ALIASES-- +Core.DefinitionCache +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt new file mode 100644 index 00000000000..54036507d6c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt @@ -0,0 +1,13 @@ +Cache.SerializerPath +TYPE: string/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Absolute path with no trailing slash to store serialized definitions in. + Default is within the + HTML Purifier library inside DefinitionCache/Serializer. This + path must be writable by the webserver. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt new file mode 100644 index 00000000000..2e0cc81044f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt @@ -0,0 +1,16 @@ +Cache.SerializerPermissions +TYPE: int/null +VERSION: 4.3.0 +DEFAULT: 0755 +--DESCRIPTION-- + +

+ Directory permissions of the files and directories created inside + the DefinitionCache/Serializer or other custom serializer path. +

+

+ In HTML Purifier 4.8.0, this also supports NULL, + which means that no chmod'ing or directory creation shall + occur. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt new file mode 100644 index 00000000000..568cbf3b328 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt @@ -0,0 +1,18 @@ +Core.AggressivelyFixLt +TYPE: bool +VERSION: 2.1.0 +DEFAULT: true +--DESCRIPTION-- +

+ This directive enables aggressive pre-filter fixes HTML Purifier can + perform in order to ensure that open angled-brackets do not get killed + during parsing stage. Enabling this will result in two preg_replace_callback + calls and at least two preg_replace calls for every HTML document parsed; + if your users make very well-formed HTML, you can set this directive false. + This has no effect when DirectLex is used. +

+

+ Notice: This directive's default turned from false to true + in HTML Purifier 3.2.0. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt new file mode 100644 index 00000000000..b2b6ab14968 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt @@ -0,0 +1,16 @@ +Core.AggressivelyRemoveScript +TYPE: bool +VERSION: 4.9.0 +DEFAULT: true +--DESCRIPTION-- +

+ This directive enables aggressive pre-filter removal of + script tags. This is not necessary for security, + but it can help work around a bug in libxml where embedded + HTML elements inside script sections cause the parser to + choke. To revert to pre-4.9.0 behavior, set this to false. + This directive has no effect if %Core.Trusted is true, + %Core.RemoveScriptContents is false, or %Core.HiddenElements + does not contain script. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt new file mode 100644 index 00000000000..2c910cc7de8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt @@ -0,0 +1,16 @@ +Core.AllowHostnameUnderscore +TYPE: bool +VERSION: 4.6.0 +DEFAULT: false +--DESCRIPTION-- +

+ By RFC 1123, underscores are not permitted in host names. + (This is in contrast to the specification for DNS, RFC + 2181, which allows underscores.) + However, most browsers do the right thing when faced with + an underscore in the host name, and so some poorly written + websites are written with the expectation this should work. + Setting this parameter to true relaxes our allowed character + check so that underscores are permitted. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt new file mode 100644 index 00000000000..06278f82a16 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt @@ -0,0 +1,12 @@ +Core.AllowParseManyTags +TYPE: bool +DEFAULT: false +VERSION: 4.10.1 +--DESCRIPTION-- +

+ This directive allows parsing of many nested tags. + If you set true, relaxes any hardcoded limit from the parser. + However, in that case it may cause a Dos attack. + Be careful when enabling it. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt new file mode 100644 index 00000000000..d7317911fa3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt @@ -0,0 +1,12 @@ +Core.CollectErrors +TYPE: bool +VERSION: 2.0.0 +DEFAULT: false +--DESCRIPTION-- + +Whether or not to collect errors found while filtering the document. This +is a useful way to give feedback to your users. Warning: +Currently this feature is very patchy and experimental, with lots of +possible error messages not yet implemented. It will not cause any +problems, but it may not help your users either. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt new file mode 100644 index 00000000000..a75844cd58e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt @@ -0,0 +1,160 @@ +Core.ColorKeywords +TYPE: hash +VERSION: 2.0.0 +--DEFAULT-- +array ( + 'aliceblue' => '#F0F8FF', + 'antiquewhite' => '#FAEBD7', + 'aqua' => '#00FFFF', + 'aquamarine' => '#7FFFD4', + 'azure' => '#F0FFFF', + 'beige' => '#F5F5DC', + 'bisque' => '#FFE4C4', + 'black' => '#000000', + 'blanchedalmond' => '#FFEBCD', + 'blue' => '#0000FF', + 'blueviolet' => '#8A2BE2', + 'brown' => '#A52A2A', + 'burlywood' => '#DEB887', + 'cadetblue' => '#5F9EA0', + 'chartreuse' => '#7FFF00', + 'chocolate' => '#D2691E', + 'coral' => '#FF7F50', + 'cornflowerblue' => '#6495ED', + 'cornsilk' => '#FFF8DC', + 'crimson' => '#DC143C', + 'cyan' => '#00FFFF', + 'darkblue' => '#00008B', + 'darkcyan' => '#008B8B', + 'darkgoldenrod' => '#B8860B', + 'darkgray' => '#A9A9A9', + 'darkgrey' => '#A9A9A9', + 'darkgreen' => '#006400', + 'darkkhaki' => '#BDB76B', + 'darkmagenta' => '#8B008B', + 'darkolivegreen' => '#556B2F', + 'darkorange' => '#FF8C00', + 'darkorchid' => '#9932CC', + 'darkred' => '#8B0000', + 'darksalmon' => '#E9967A', + 'darkseagreen' => '#8FBC8F', + 'darkslateblue' => '#483D8B', + 'darkslategray' => '#2F4F4F', + 'darkslategrey' => '#2F4F4F', + 'darkturquoise' => '#00CED1', + 'darkviolet' => '#9400D3', + 'deeppink' => '#FF1493', + 'deepskyblue' => '#00BFFF', + 'dimgray' => '#696969', + 'dimgrey' => '#696969', + 'dodgerblue' => '#1E90FF', + 'firebrick' => '#B22222', + 'floralwhite' => '#FFFAF0', + 'forestgreen' => '#228B22', + 'fuchsia' => '#FF00FF', + 'gainsboro' => '#DCDCDC', + 'ghostwhite' => '#F8F8FF', + 'gold' => '#FFD700', + 'goldenrod' => '#DAA520', + 'gray' => '#808080', + 'grey' => '#808080', + 'green' => '#008000', + 'greenyellow' => '#ADFF2F', + 'honeydew' => '#F0FFF0', + 'hotpink' => '#FF69B4', + 'indianred' => '#CD5C5C', + 'indigo' => '#4B0082', + 'ivory' => '#FFFFF0', + 'khaki' => '#F0E68C', + 'lavender' => '#E6E6FA', + 'lavenderblush' => '#FFF0F5', + 'lawngreen' => '#7CFC00', + 'lemonchiffon' => '#FFFACD', + 'lightblue' => '#ADD8E6', + 'lightcoral' => '#F08080', + 'lightcyan' => '#E0FFFF', + 'lightgoldenrodyellow' => '#FAFAD2', + 'lightgray' => '#D3D3D3', + 'lightgrey' => '#D3D3D3', + 'lightgreen' => '#90EE90', + 'lightpink' => '#FFB6C1', + 'lightsalmon' => '#FFA07A', + 'lightseagreen' => '#20B2AA', + 'lightskyblue' => '#87CEFA', + 'lightslategray' => '#778899', + 'lightslategrey' => '#778899', + 'lightsteelblue' => '#B0C4DE', + 'lightyellow' => '#FFFFE0', + 'lime' => '#00FF00', + 'limegreen' => '#32CD32', + 'linen' => '#FAF0E6', + 'magenta' => '#FF00FF', + 'maroon' => '#800000', + 'mediumaquamarine' => '#66CDAA', + 'mediumblue' => '#0000CD', + 'mediumorchid' => '#BA55D3', + 'mediumpurple' => '#9370DB', + 'mediumseagreen' => '#3CB371', + 'mediumslateblue' => '#7B68EE', + 'mediumspringgreen' => '#00FA9A', + 'mediumturquoise' => '#48D1CC', + 'mediumvioletred' => '#C71585', + 'midnightblue' => '#191970', + 'mintcream' => '#F5FFFA', + 'mistyrose' => '#FFE4E1', + 'moccasin' => '#FFE4B5', + 'navajowhite' => '#FFDEAD', + 'navy' => '#000080', + 'oldlace' => '#FDF5E6', + 'olive' => '#808000', + 'olivedrab' => '#6B8E23', + 'orange' => '#FFA500', + 'orangered' => '#FF4500', + 'orchid' => '#DA70D6', + 'palegoldenrod' => '#EEE8AA', + 'palegreen' => '#98FB98', + 'paleturquoise' => '#AFEEEE', + 'palevioletred' => '#DB7093', + 'papayawhip' => '#FFEFD5', + 'peachpuff' => '#FFDAB9', + 'peru' => '#CD853F', + 'pink' => '#FFC0CB', + 'plum' => '#DDA0DD', + 'powderblue' => '#B0E0E6', + 'purple' => '#800080', + 'rebeccapurple' => '#663399', + 'red' => '#FF0000', + 'rosybrown' => '#BC8F8F', + 'royalblue' => '#4169E1', + 'saddlebrown' => '#8B4513', + 'salmon' => '#FA8072', + 'sandybrown' => '#F4A460', + 'seagreen' => '#2E8B57', + 'seashell' => '#FFF5EE', + 'sienna' => '#A0522D', + 'silver' => '#C0C0C0', + 'skyblue' => '#87CEEB', + 'slateblue' => '#6A5ACD', + 'slategray' => '#708090', + 'slategrey' => '#708090', + 'snow' => '#FFFAFA', + 'springgreen' => '#00FF7F', + 'steelblue' => '#4682B4', + 'tan' => '#D2B48C', + 'teal' => '#008080', + 'thistle' => '#D8BFD8', + 'tomato' => '#FF6347', + 'turquoise' => '#40E0D0', + 'violet' => '#EE82EE', + 'wheat' => '#F5DEB3', + 'white' => '#FFFFFF', + 'whitesmoke' => '#F5F5F5', + 'yellow' => '#FFFF00', + 'yellowgreen' => '#9ACD32' +) +--DESCRIPTION-- + +Lookup array of color names to six digit hexadecimal number corresponding +to color, with preceding hash mark. Used when parsing colors. The lookup +is done in a case-insensitive manner. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt new file mode 100644 index 00000000000..64b114fce2b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt @@ -0,0 +1,14 @@ +Core.ConvertDocumentToFragment +TYPE: bool +DEFAULT: true +--DESCRIPTION-- + +This parameter determines whether or not the filter should convert +input that is a full document with html and body tags to a fragment +of just the contents of a body tag. This parameter is simply something +HTML Purifier can do during an edge-case: for most inputs, this +processing is not necessary. + +--ALIASES-- +Core.AcceptFullDocuments +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt new file mode 100644 index 00000000000..36f16e07eae --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt @@ -0,0 +1,17 @@ +Core.DirectLexLineNumberSyncInterval +TYPE: int +VERSION: 2.0.0 +DEFAULT: 0 +--DESCRIPTION-- + +

+ Specifies the number of tokens the DirectLex line number tracking + implementations should process before attempting to resyncronize the + current line count by manually counting all previous new-lines. When + at 0, this functionality is disabled. Lower values will decrease + performance, and this is only strictly necessary if the counting + algorithm is buggy (in which case you should report it as a bug). + This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is + not being used. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt new file mode 100644 index 00000000000..1cd4c2c9640 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt @@ -0,0 +1,14 @@ +Core.DisableExcludes +TYPE: bool +DEFAULT: false +VERSION: 4.5.0 +--DESCRIPTION-- +

+ This directive disables SGML-style exclusions, e.g. the exclusion of + <object> in any descendant of a + <pre> tag. Disabling excludes will allow some + invalid documents to pass through HTML Purifier, but HTML Purifier + will also be less likely to accidentally remove large documents during + processing. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt new file mode 100644 index 00000000000..ce243c35dc0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt @@ -0,0 +1,9 @@ +Core.EnableIDNA +TYPE: bool +DEFAULT: false +VERSION: 4.4.0 +--DESCRIPTION-- +Allows international domain names in URLs. This configuration option +requires the PEAR Net_IDNA2 module to be installed. It operates by +punycoding any internationalized host names for maximum portability. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt new file mode 100644 index 00000000000..8bfb47c3ac1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt @@ -0,0 +1,15 @@ +Core.Encoding +TYPE: istring +DEFAULT: 'utf-8' +--DESCRIPTION-- +If for some reason you are unable to convert all webpages to UTF-8, you can +use this directive as a stop-gap compatibility change to let HTML Purifier +deal with non UTF-8 input. This technique has notable deficiencies: +absolutely no characters outside of the selected character encoding will be +preserved, not even the ones that have been ampersand escaped (this is due +to a UTF-8 specific feature that automatically resolves all +entities), making it pretty useless for anything except the most I18N-blind +applications, although %Core.EscapeNonASCIICharacters offers fixes this +trouble with another tradeoff. This directive only accepts ISO-8859-1 if +iconv is not enabled. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt new file mode 100644 index 00000000000..a3881be75cf --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt @@ -0,0 +1,12 @@ +Core.EscapeInvalidChildren +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +

Warning: this configuration option is no longer does anything as of 4.6.0.

+ +

When true, a child is found that is not allowed in the context of the +parent element will be transformed into text as if it were ASCII. When +false, that element and all internal tags will be dropped, though text will +be preserved. There is no option for dropping the element but preserving +child nodes.

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt new file mode 100644 index 00000000000..a7a5b249bb7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt @@ -0,0 +1,7 @@ +Core.EscapeInvalidTags +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When true, invalid tags will be written back to the document as plain text. +Otherwise, they are silently dropped. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt new file mode 100644 index 00000000000..abb499948ac --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt @@ -0,0 +1,13 @@ +Core.EscapeNonASCIICharacters +TYPE: bool +VERSION: 1.4.0 +DEFAULT: false +--DESCRIPTION-- +This directive overcomes a deficiency in %Core.Encoding by blindly +converting all non-ASCII characters into decimal numeric entities before +converting it to its native encoding. This means that even characters that +can be expressed in the non-UTF-8 encoding will be entity-ized, which can +be a real downer for encodings like Big5. It also assumes that the ASCII +repetoire is available, although this is the case for almost all encodings. +Anyway, use UTF-8! +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt new file mode 100644 index 00000000000..915391edb73 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt @@ -0,0 +1,19 @@ +Core.HiddenElements +TYPE: lookup +--DEFAULT-- +array ( + 'script' => true, + 'style' => true, +) +--DESCRIPTION-- + +

+ This directive is a lookup array of elements which should have their + contents removed when they are not allowed by the HTML definition. + For example, the contents of a script tag are not + normally shown in a document, so if script tags are to be removed, + their contents should be removed to. This is opposed to a b + tag, which defines some presentational changes but does not hide its + contents. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt new file mode 100644 index 00000000000..233fca14f84 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt @@ -0,0 +1,10 @@ +Core.Language +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'en' +--DESCRIPTION-- + +ISO 639 language code for localizable things in HTML Purifier to use, +which is mainly error reporting. There is currently only an English (en) +translation, so this directive is currently useless. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt new file mode 100644 index 00000000000..392b4364934 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt @@ -0,0 +1,36 @@ +Core.LegacyEntityDecoder +TYPE: bool +VERSION: 4.9.0 +DEFAULT: false +--DESCRIPTION-- +

+ Prior to HTML Purifier 4.9.0, entities were decoded by performing + a global search replace for all entities whose decoded versions + did not have special meanings under HTML, and replaced them with + their decoded versions. We would match all entities, even if they did + not have a trailing semicolon, but only if there weren't any trailing + alphanumeric characters. +

+ + + + + + +
OriginalTextAttribute
&yen;¥¥
&yen¥¥
&yena&yena&yena
&yen=¥=¥=
+

+ In HTML Purifier 4.9.0, we changed the behavior of entity parsing + to match entities that had missing trailing semicolons in less + cases, to more closely match HTML5 parsing behavior: +

+ + + + + + +
OriginalTextAttribute
&yen;¥¥
&yen¥¥
&yena¥a&yena
&yen=¥=&yen=
+

+ This flag reverts back to pre-HTML Purifier 4.9.0 behavior. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt new file mode 100644 index 00000000000..8983e2cca9b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt @@ -0,0 +1,34 @@ +Core.LexerImpl +TYPE: mixed/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ This parameter determines what lexer implementation can be used. The + valid values are: +

+
+
null
+
+ Recommended, the lexer implementation will be auto-detected based on + your PHP-version and configuration. +
+
string lexer identifier
+
+ This is a slim way of manually overridding the implementation. + Currently recognized values are: DOMLex (the default PHP5 +implementation) + and DirectLex (the default PHP4 implementation). Only use this if + you know what you are doing: usually, the auto-detection will + manage things for cases you aren't even aware of. +
+
object lexer instance
+
+ Super-advanced: you can specify your own, custom, implementation that + implements the interface defined by HTMLPurifier_Lexer. + I may remove this option simply because I don't expect anyone + to use it. +
+
+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt new file mode 100644 index 00000000000..eb841a7597a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt @@ -0,0 +1,16 @@ +Core.MaintainLineNumbers +TYPE: bool/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ If true, HTML Purifier will add line number information to all tokens. + This is useful when error reporting is turned on, but can result in + significant performance degradation and should not be used when + unnecessary. This directive must be used with the DirectLex lexer, + as the DOMLex lexer does not (yet) support this functionality. + If the value is null, an appropriate value will be selected based + on other configuration. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt new file mode 100644 index 00000000000..d77f5360d7c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt @@ -0,0 +1,11 @@ +Core.NormalizeNewlines +TYPE: bool +VERSION: 4.2.0 +DEFAULT: true +--DESCRIPTION-- +

+ Whether or not to normalize newlines to the operating + system default. When false, HTML Purifier + will attempt to preserve mixed newline files. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt new file mode 100644 index 00000000000..4070c2a0de2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt @@ -0,0 +1,12 @@ +Core.RemoveInvalidImg +TYPE: bool +DEFAULT: true +VERSION: 1.3.0 +--DESCRIPTION-- + +

+ This directive enables pre-emptive URI checking in img + tags, as the attribute validation strategy is not authorized to + remove elements from the document. Revert to pre-1.3.0 behavior by setting to false. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt new file mode 100644 index 00000000000..3397d9f71f0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt @@ -0,0 +1,11 @@ +Core.RemoveProcessingInstructions +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +Instead of escaping processing instructions in the form <? ... +?>, remove it out-right. This may be useful if the HTML +you are validating contains XML processing instruction gunk, however, +it can also be user-unfriendly for people attempting to post PHP +snippets. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt new file mode 100644 index 00000000000..a4cd966df89 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt @@ -0,0 +1,12 @@ +Core.RemoveScriptContents +TYPE: bool/null +DEFAULT: NULL +VERSION: 2.0.0 +DEPRECATED-VERSION: 2.1.0 +DEPRECATED-USE: Core.HiddenElements +--DESCRIPTION-- +

+ This directive enables HTML Purifier to remove not only script tags + but all of their contents. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt new file mode 100644 index 00000000000..3db50ef204b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt @@ -0,0 +1,11 @@ +Filter.Custom +TYPE: list +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ This directive can be used to add custom filters; it is nearly the + equivalent of the now deprecated HTMLPurifier->addFilter() + method. Specify an array of concrete implementations. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt new file mode 100644 index 00000000000..16829bcda03 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt @@ -0,0 +1,14 @@ +Filter.ExtractStyleBlocks.Escaping +TYPE: bool +VERSION: 3.0.0 +DEFAULT: true +ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping +--DESCRIPTION-- + +

+ Whether or not to escape the dangerous characters <, > and & + as \3C, \3E and \26, respectively. This is can be safely set to false + if the contents of StyleBlocks will be placed in an external stylesheet, + where there is no risk of it being interpreted as HTML. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt new file mode 100644 index 00000000000..7f95f54d125 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt @@ -0,0 +1,29 @@ +Filter.ExtractStyleBlocks.Scope +TYPE: string/null +VERSION: 3.0.0 +DEFAULT: NULL +ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope +--DESCRIPTION-- + +

+ If you would like users to be able to define external stylesheets, but + only allow them to specify CSS declarations for a specific node and + prevent them from fiddling with other elements, use this directive. + It accepts any valid CSS selector, and will prepend this to any + CSS declaration extracted from the document. For example, if this + directive is set to #user-content and a user uses the + selector a:hover, the final selector will be + #user-content a:hover. +

+

+ The comma shorthand may be used; consider the above example, with + #user-content, #user-content2, the final selector will + be #user-content a:hover, #user-content2 a:hover. +

+

+ Warning: It is possible for users to bypass this measure + using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML + Purifier, and I am working to get it fixed. Until then, HTML Purifier + performs a basic check to prevent this. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt new file mode 100644 index 00000000000..6c231b2d7f7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt @@ -0,0 +1,16 @@ +Filter.ExtractStyleBlocks.TidyImpl +TYPE: mixed/null +VERSION: 3.1.0 +DEFAULT: NULL +ALIASES: FilterParam.ExtractStyleBlocksTidyImpl +--DESCRIPTION-- +

+ If left NULL, HTML Purifier will attempt to instantiate a csstidy + class to use for internal cleaning. This will usually be good enough. +

+

+ However, for trusted user input, you can set this to false to + disable cleaning. In addition, you can supply your own concrete implementation + of Tidy's interface to use, although I don't know why you'd want to do that. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt new file mode 100644 index 00000000000..078d087417e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt @@ -0,0 +1,74 @@ +Filter.ExtractStyleBlocks +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +EXTERNAL: CSSTidy +--DESCRIPTION-- +

+ This directive turns on the style block extraction filter, which removes + style blocks from input HTML, cleans them up with CSSTidy, + and places them in the StyleBlocks context variable, for further + use by you, usually to be placed in an external stylesheet, or a + style block in the head of your document. +

+

+ Sample usage: +

+
';
+?>
+
+
+
+  Filter.ExtractStyleBlocks
+body {color:#F00;} Some text';
+
+    $config = HTMLPurifier_Config::createDefault();
+    $config->set('Filter', 'ExtractStyleBlocks', true);
+    $purifier = new HTMLPurifier($config);
+
+    $html = $purifier->purify($dirty);
+
+    // This implementation writes the stylesheets to the styles/ directory.
+    // You can also echo the styles inside the document, but it's a bit
+    // more difficult to make sure they get interpreted properly by
+    // browsers; try the usual CSS armoring techniques.
+    $styles = $purifier->context->get('StyleBlocks');
+    $dir = 'styles/';
+    if (!is_dir($dir)) mkdir($dir);
+    $hash = sha1($_GET['html']);
+    foreach ($styles as $i => $style) {
+        file_put_contents($name = $dir . $hash . "_$i");
+        echo '';
+    }
+?>
+
+
+  
+ +
+ + +]]>
+

+ Warning: It is possible for a user to mount an + imagecrash attack using this CSS. Counter-measures are difficult; + it is not simply enough to limit the range of CSS lengths (using + relative lengths with many nesting levels allows for large values + to be attained without actually specifying them in the stylesheet), + and the flexible nature of selectors makes it difficult to selectively + disable lengths on image tags (HTML Purifier, however, does disable + CSS width and height in inline styling). There are probably two effective + counter measures: an explicit width and height set to auto in all + images in your document (unlikely) or the disabling of width and + height (somewhat reasonable). Whether or not these measures should be + used is left to the reader. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt new file mode 100644 index 00000000000..321eaa2d803 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -0,0 +1,16 @@ +Filter.YouTube +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ Warning: Deprecated in favor of %HTML.SafeObject and + %Output.FlashCompat (turn both on to allow YouTube videos and other + Flash content). +

+

+ This directive enables YouTube video embedding in HTML Purifier. Check + this document + on embedding videos for more information on what this filter does. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt new file mode 100644 index 00000000000..0b2c106da5e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -0,0 +1,25 @@ +HTML.Allowed +TYPE: itext/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ This is a preferred convenience directive that combines + %HTML.AllowedElements and %HTML.AllowedAttributes. + Specify elements and attributes that are allowed using: + element1[attr1|attr2],element2.... For example, + if you would like to only allow paragraphs and links, specify + a[href],p. You can specify attributes that apply + to all elements using an asterisk, e.g. *[lang]. + You can also use newlines instead of commas to separate elements. +

+

+ Warning: + All of the constraints on the component directives are still enforced. + The syntax is a subset of TinyMCE's valid_elements + whitelist: directly copy-pasting it here will probably result in + broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes + are set, this directive has no effect. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt new file mode 100644 index 00000000000..fcf093f17d1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt @@ -0,0 +1,19 @@ +HTML.AllowedAttributes +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ If HTML Purifier's attribute set is unsatisfactory, overload it! + The syntax is "tag.attr" or "*.attr" for the global attributes + (style, id, class, dir, lang, xml:lang). +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. For + example, %HTML.EnableAttrID will take precedence over *.id in this + directive. You must set that directive to true before you can use + IDs at all. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt new file mode 100644 index 00000000000..140e21423e5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt @@ -0,0 +1,10 @@ +HTML.AllowedComments +TYPE: lookup +VERSION: 4.4.0 +DEFAULT: array() +--DESCRIPTION-- +A whitelist which indicates what explicit comment bodies should be +allowed, modulo leading and trailing whitespace. See also %HTML.AllowedCommentsRegexp +(these directives are union'ed together, so a comment is considered +valid if any directive deems it valid.) +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt new file mode 100644 index 00000000000..f22e977d438 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt @@ -0,0 +1,15 @@ +HTML.AllowedCommentsRegexp +TYPE: string/null +VERSION: 4.4.0 +DEFAULT: NULL +--DESCRIPTION-- +A regexp, which if it matches the body of a comment, indicates that +it should be allowed. Trailing and leading spaces are removed prior +to running this regular expression. +Warning: Make sure you specify +correct anchor metacharacters ^regex$, otherwise you may accept +comments that you did not mean to! In particular, the regex /foo|bar/ +is probably not sufficiently strict, since it also allows foobar. +See also %HTML.AllowedComments (these directives are union'ed together, +so a comment is considered valid if any directive deems it valid.) +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt new file mode 100644 index 00000000000..1d3fa7907dc --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -0,0 +1,23 @@ +HTML.AllowedElements +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ If HTML Purifier's tag set is unsatisfactory for your needs, you can + overload it with your own list of tags to allow. If you change + this, you probably also want to change %HTML.AllowedAttributes; see + also %HTML.Allowed which lets you set allowed elements and + attributes at the same time. +

+

+ If you attempt to allow an element that HTML Purifier does not know + about, HTML Purifier will raise an error. You will need to manually + tell HTML Purifier about this element by using the + advanced customization features. +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt new file mode 100644 index 00000000000..5a59a55c083 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt @@ -0,0 +1,20 @@ +HTML.AllowedModules +TYPE: lookup/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ A doctype comes with a set of usual modules to use. Without having + to mucking about with the doctypes, you can quickly activate or + disable these modules by specifying which modules you wish to allow + with this directive. This is most useful for unit testing specific + modules, although end users may find it useful for their own ends. +

+

+ If you specify a module that does not exist, the manager will silently + fail to use it, so be careful! User-defined modules are not affected + by this directive. Modules defined in %HTML.CoreModules are not + affected by this directive. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt new file mode 100644 index 00000000000..151fb7b8264 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt @@ -0,0 +1,11 @@ +HTML.Attr.Name.UseCDATA +TYPE: bool +DEFAULT: false +VERSION: 4.0.0 +--DESCRIPTION-- +The W3C specification DTD defines the name attribute to be CDATA, not ID, due +to limitations of DTD. In certain documents, this relaxed behavior is desired, +whether it is to specify duplicate names, or to specify names that would be +illegal IDs (for example, names that begin with a digit.) Set this configuration +directive to true to use the relaxed parsing rules. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt new file mode 100644 index 00000000000..45ae469ec98 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt @@ -0,0 +1,18 @@ +HTML.BlockWrapper +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'p' +--DESCRIPTION-- + +

+ String name of element to wrap inline elements that are inside a block + context. This only occurs in the children of blockquote in strict mode. +

+

+ Example: by default value, + <blockquote>Foo</blockquote> would become + <blockquote><p>Foo</p></blockquote>. + The <p> tags can be replaced with whatever you desire, + as long as it is a block level element. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt new file mode 100644 index 00000000000..52461887955 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt @@ -0,0 +1,23 @@ +HTML.CoreModules +TYPE: lookup +VERSION: 2.0.0 +--DEFAULT-- +array ( + 'Structure' => true, + 'Text' => true, + 'Hypertext' => true, + 'List' => true, + 'NonXMLCommonAttributes' => true, + 'XMLCommonAttributes' => true, + 'CommonAttributes' => true, +) +--DESCRIPTION-- + +

+ Certain modularized doctypes (XHTML, namely), have certain modules + that must be included for the doctype to be an conforming document + type: put those modules here. By default, XHTML's core modules + are used. You can set this to a blank array to disable core module + protection, but this is not recommended. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt new file mode 100644 index 00000000000..6ed70b599f4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt @@ -0,0 +1,9 @@ +HTML.CustomDoctype +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +A custom doctype for power-users who defined their own document +type. This directive only applies when %HTML.Doctype is blank. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt new file mode 100644 index 00000000000..103db754a28 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt @@ -0,0 +1,33 @@ +HTML.DefinitionID +TYPE: string/null +DEFAULT: NULL +VERSION: 2.0.0 +--DESCRIPTION-- + +

+ Unique identifier for a custom-built HTML definition. If you edit + the raw version of the HTMLDefinition, introducing changes that the + configuration object does not reflect, you must specify this variable. + If you change your custom edits, you should change this directive, or + clear your cache. Example: +

+
+$config = HTMLPurifier_Config::createDefault();
+$config->set('HTML', 'DefinitionID', '1');
+$def = $config->getHTMLDefinition();
+$def->addAttribute('a', 'tabindex', 'Number');
+
+

+ In the above example, the configuration is still at the defaults, but + using the advanced API, an extra attribute has been added. The + configuration object normally has no way of knowing that this change + has taken place, so it needs an extra directive: %HTML.DefinitionID. + If someone else attempts to use the default configuration, these two + pieces of code will not clobber each other in the cache, since one has + an extra directive attached to it. +

+

+ You must specify a value to this directive to use the + advanced API features. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt new file mode 100644 index 00000000000..229ae0267a6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt @@ -0,0 +1,16 @@ +HTML.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition specified in + %HTML.DefinitionID. This serves the same purpose: uniquely identifying + your custom definition, but this one does so in a chronological + context: revision 3 is more up-to-date then revision 2. Thus, when + this gets incremented, the cache handling is smart enough to clean + up any older revisions of your definition as well as flush the + cache. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt new file mode 100644 index 00000000000..9dab497f2f3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt @@ -0,0 +1,11 @@ +HTML.Doctype +TYPE: string/null +DEFAULT: NULL +--DESCRIPTION-- +Doctype to use during filtering. Technically speaking this is not actually +a doctype (as it does not identify a corresponding DTD), but we are using +this name for sake of simplicity. When non-blank, this will override any +older directives like %HTML.XHTML or %HTML.Strict. +--ALLOWED-- +'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1' +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt new file mode 100644 index 00000000000..7878dc0bf64 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt @@ -0,0 +1,11 @@ +HTML.FlashAllowFullScreen +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit embedded Flash content from + %HTML.SafeObject to expand to the full screen. Corresponds to + the allowFullScreen parameter. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt new file mode 100644 index 00000000000..57358f9bad2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt @@ -0,0 +1,21 @@ +HTML.ForbiddenAttributes +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ While this directive is similar to %HTML.AllowedAttributes, for + forwards-compatibility with XML, this attribute has a different syntax. Instead of + tag.attr, use tag@attr. To disallow href + attributes in a tags, set this directive to + a@href. You can also disallow an attribute globally with + attr or *@attr (either syntax is fine; the latter + is provided for consistency with %HTML.AllowedAttributes). +

+

+ Warning: This directive complements %HTML.ForbiddenElements, + accordingly, check + out that directive for a discussion of why you + should think twice before using this directive. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt new file mode 100644 index 00000000000..93a53e14fb4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt @@ -0,0 +1,20 @@ +HTML.ForbiddenElements +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ This was, perhaps, the most requested feature ever in HTML + Purifier. Please don't abuse it! This is the logical inverse of + %HTML.AllowedElements, and it will override that directive, or any + other directive. +

+

+ If possible, %HTML.Allowed is recommended over this directive, because it + can sometimes be difficult to tell whether or not you've forbidden all of + the behavior you would like to disallow. If you forbid img + with the expectation of preventing images on your site, you'll be in for + a nasty surprise when people start using the background-image + CSS property. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt new file mode 100644 index 00000000000..4a432d89b71 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt @@ -0,0 +1,11 @@ +HTML.Forms +TYPE: bool +VERSION: 4.13.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit form elements in the user input, regardless of + %HTML.Trusted value. Please be very careful when using this functionality, as + enabling forms in untrusted documents may allow for phishing attacks. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt new file mode 100644 index 00000000000..e424c386ec3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt @@ -0,0 +1,14 @@ +HTML.MaxImgLength +TYPE: int/null +DEFAULT: 1200 +VERSION: 3.1.1 +--DESCRIPTION-- +

+ This directive controls the maximum number of pixels in the width and + height attributes in img tags. This is + in place to prevent imagecrash attacks, disable with null at your own risk. + This directive is similar to %CSS.MaxImgLength, and both should be + concurrently edited, although there are + subtle differences in the input format (the HTML max is an integer). +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt new file mode 100644 index 00000000000..700b30924a1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt @@ -0,0 +1,7 @@ +HTML.Nofollow +TYPE: bool +VERSION: 4.3.0 +DEFAULT: FALSE +--DESCRIPTION-- +If enabled, nofollow rel attributes are added to all outgoing links. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt new file mode 100644 index 00000000000..62e8e160c73 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt @@ -0,0 +1,12 @@ +HTML.Parent +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'div' +--DESCRIPTION-- + +

+ String name of element that HTML fragment passed to library will be + inserted in. An interesting variation would be using span as the + parent element, meaning that only inline tags would be allowed. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt new file mode 100644 index 00000000000..dfb720496da --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt @@ -0,0 +1,12 @@ +HTML.Proprietary +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to allow proprietary elements and attributes in your + documents, as per HTMLPurifier_HTMLModule_Proprietary. + Warning: This can cause your documents to stop + validating! +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt new file mode 100644 index 00000000000..cdda09a4c5f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -0,0 +1,13 @@ +HTML.SafeEmbed +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit embed tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to embed tags. Embed is a proprietary + element and will cause your website to stop validating; you should + see if you can use %Output.FlashCompat with %HTML.SafeObject instead + first.

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt new file mode 100644 index 00000000000..5eb6ec2b5a0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt @@ -0,0 +1,13 @@ +HTML.SafeIframe +TYPE: bool +VERSION: 4.4.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit iframe tags in untrusted documents. This + directive must be accompanied by a whitelist of permitted iframes, + such as %URI.SafeIframeRegexp, otherwise it will fatally error. + This directive has no effect on strict doctypes, as iframes are not + valid. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt new file mode 100644 index 00000000000..ceb342e22b7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -0,0 +1,13 @@ +HTML.SafeObject +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit object tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to object tags. You should also enable + %Output.FlashCompat in order to generate Internet Explorer + compatibility code for your object tags. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt new file mode 100644 index 00000000000..5ebc7a19d54 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt @@ -0,0 +1,10 @@ +HTML.SafeScripting +TYPE: lookup +VERSION: 4.5.0 +DEFAULT: array() +--DESCRIPTION-- +

+ Whether or not to permit script tags to external scripts in documents. + Inline scripting is not allowed, and the script must match an explicit whitelist. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt new file mode 100644 index 00000000000..a8b1de56bef --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt @@ -0,0 +1,9 @@ +HTML.Strict +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not to use Transitional (loose) or Strict rulesets. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt new file mode 100644 index 00000000000..587a16778b2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt @@ -0,0 +1,8 @@ +HTML.TargetBlank +TYPE: bool +VERSION: 4.4.0 +DEFAULT: FALSE +--DESCRIPTION-- +If enabled, target=blank attributes are added to all outgoing links. +(This includes links from an HTTPS version of a page to an HTTP version.) +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt new file mode 100644 index 00000000000..dd514c0def2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt @@ -0,0 +1,10 @@ +--# vim: et sw=4 sts=4 +HTML.TargetNoopener +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noopener rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt new file mode 100644 index 00000000000..cb5a0b0e5e1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt @@ -0,0 +1,9 @@ +HTML.TargetNoreferrer +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noreferrer rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt new file mode 100644 index 00000000000..b4c271b7fac --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt @@ -0,0 +1,8 @@ +HTML.TidyAdd +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to add to the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt new file mode 100644 index 00000000000..4186ccd0d19 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt @@ -0,0 +1,24 @@ +HTML.TidyLevel +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'medium' +--DESCRIPTION-- + +

General level of cleanliness the Tidy module should enforce. +There are four allowed values:

+
+
none
+
No extra tidying should be done
+
light
+
Only fix elements that would be discarded otherwise due to + lack of support in doctype
+
medium
+
Enforce best practices
+
heavy
+
Transform all deprecated elements and attributes to standards + compliant equivalents
+
+ +--ALLOWED-- +'none', 'light', 'medium', 'heavy' +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt new file mode 100644 index 00000000000..996762bd1d6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt @@ -0,0 +1,8 @@ +HTML.TidyRemove +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to remove from the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt new file mode 100644 index 00000000000..1db9237e9ee --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt @@ -0,0 +1,9 @@ +HTML.Trusted +TYPE: bool +VERSION: 2.0.0 +DEFAULT: false +--DESCRIPTION-- +Indicates whether or not the user input is trusted or not. If the input is +trusted, a more expansive set of allowed tags and attributes will be used. +See also %CSS.Trusted. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt new file mode 100644 index 00000000000..2a47e384f41 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt @@ -0,0 +1,11 @@ +HTML.XHTML +TYPE: bool +DEFAULT: true +VERSION: 1.1.0 +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor. +--ALIASES-- +Core.XHTML +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt new file mode 100644 index 00000000000..08921fde70a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt @@ -0,0 +1,10 @@ +Output.CommentScriptContents +TYPE: bool +VERSION: 2.0.0 +DEFAULT: true +--DESCRIPTION-- +Determines whether or not HTML Purifier should attempt to fix up the +contents of script tags for legacy browsers with comments. +--ALIASES-- +Core.CommentScriptContents +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt new file mode 100644 index 00000000000..d6f0d9f2955 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt @@ -0,0 +1,15 @@ +Output.FixInnerHTML +TYPE: bool +VERSION: 4.3.0 +DEFAULT: true +--DESCRIPTION-- +

+ If true, HTML Purifier will protect against Internet Explorer's + mishandling of the innerHTML attribute by appending + a space to any attribute that does not contain angled brackets, spaces + or quotes, but contains a backtick. This slightly changes the + semantics of any given attribute, so if this is unacceptable and + you do not use innerHTML on any of your pages, you can + turn this directive off. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt new file mode 100644 index 00000000000..93398e85980 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt @@ -0,0 +1,11 @@ +Output.FlashCompat +TYPE: bool +VERSION: 4.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ If true, HTML Purifier will generate Internet Explorer compatibility + code for all object code. This is highly recommended if you enable + %HTML.SafeObject. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt new file mode 100644 index 00000000000..79f8ad82cfc --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt @@ -0,0 +1,13 @@ +Output.Newline +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Newline string to format final output with. If left null, HTML Purifier + will auto-detect the default newline type of the system and use that; + you can manually override it here. Remember, \r\n is Windows, \r + is Mac, and \n is Unix. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt new file mode 100644 index 00000000000..232b02362a4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt @@ -0,0 +1,14 @@ +Output.SortAttr +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ If true, HTML Purifier will sort attributes by name before writing them back + to the document, converting a tag like: <el b="" a="" c="" /> + to <el a="" b="" c="" />. This is a workaround for + a bug in FCKeditor which causes it to swap attributes order, adding noise + to text diffs. If you're not seeing this bug, chances are, you don't need + this directive. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt new file mode 100644 index 00000000000..06bab00a0a2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt @@ -0,0 +1,25 @@ +Output.TidyFormat +TYPE: bool +VERSION: 1.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Determines whether or not to run Tidy on the final output for pretty + formatting reasons, such as indentation and wrap. +

+

+ This can greatly improve readability for editors who are hand-editing + the HTML, but is by no means necessary as HTML Purifier has already + fixed all major errors the HTML may have had. Tidy is a non-default + extension, and this directive will silently fail if Tidy is not + available. +

+

+ If you are looking to make the overall look of your page's source + better, I recommend running Tidy on the entire page rather than just + user-content (after all, the indentation relative to the containing + blocks will be incorrect). +

+--ALIASES-- +Core.TidyFormat +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt new file mode 100644 index 00000000000..071bc0295df --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt @@ -0,0 +1,7 @@ +Test.ForceNoIconv +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When set to true, HTMLPurifier_Encoder will act as if iconv does not exist +and use only pure PHP implementations. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt new file mode 100644 index 00000000000..eb97307e200 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -0,0 +1,18 @@ +URI.AllowedSchemes +TYPE: lookup +--DEFAULT-- +array ( + 'http' => true, + 'https' => true, + 'mailto' => true, + 'ftp' => true, + 'nntp' => true, + 'news' => true, + 'tel' => true, +) +--DESCRIPTION-- +Whitelist that defines the schemes that a URI is allowed to have. This +prevents XSS attacks from using pseudo-schemes like javascript or mocha. +There is also support for the data and file +URI schemes, but they are not enabled by default. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt new file mode 100644 index 00000000000..876f0680cf2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt @@ -0,0 +1,17 @@ +URI.Base +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ The base URI is the URI of the document this purified HTML will be + inserted into. This information is important if HTML Purifier needs + to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute + is on. You may use a non-absolute URI for this value, but behavior + may vary (%URI.MakeAbsolute deals nicely with both absolute and + relative paths, but forwards-compatibility is not guaranteed). + Warning: If set, the scheme on this URI + overrides the one specified by %URI.DefaultScheme. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt new file mode 100644 index 00000000000..834bc08c0ba --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt @@ -0,0 +1,15 @@ +URI.DefaultScheme +TYPE: string/null +DEFAULT: 'http' +--DESCRIPTION-- + +

+ Defines through what scheme the output will be served, in order to + select the proper object validator when no scheme information is present. +

+ +

+ Starting with HTML Purifier 4.9.0, the default scheme can be null, in + which case we reject all URIs which do not have explicit schemes. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt new file mode 100644 index 00000000000..f05312ba862 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt @@ -0,0 +1,11 @@ +URI.DefinitionID +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Unique identifier for a custom-built URI definition. If you want + to add custom URIFilters, you must specify this value. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt new file mode 100644 index 00000000000..80cfea93f72 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt @@ -0,0 +1,11 @@ +URI.DefinitionRev +TYPE: int +VERSION: 2.1.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt new file mode 100644 index 00000000000..71ce025a2dc --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt @@ -0,0 +1,14 @@ +URI.Disable +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +

+ Disables all URIs in all forms. Not sure why you'd want to do that + (after all, the Internet's founded on the notion of a hyperlink). +

+ +--ALIASES-- +Attr.DisableURI +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt new file mode 100644 index 00000000000..13c122c8cec --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt @@ -0,0 +1,11 @@ +URI.DisableExternal +TYPE: bool +VERSION: 1.2.0 +DEFAULT: false +--DESCRIPTION-- +Disables links to external websites. This is a highly effective anti-spam +and anti-pagerank-leech measure, but comes at a hefty price: nolinks or +images outside of your domain will be allowed. Non-linkified URIs will +still be preserved. If you want to be able to link to subdomains or use +absolute URIs, specify %URI.Host for your website. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt new file mode 100644 index 00000000000..abcc1efd613 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt @@ -0,0 +1,13 @@ +URI.DisableExternalResources +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- +Disables the embedding of external resources, preventing users from +embedding things like images from other hosts. This prevents access +tracking (good for email viewers), bandwidth leeching, cross-site request +forging, goatse.cx posting, and other nasties, but also results in a loss +of end-user functionality (they can't directly post a pic they posted from +Flickr anymore). Use it if you don't have a robust user-content moderation +team. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt new file mode 100644 index 00000000000..f891de49965 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -0,0 +1,15 @@ +URI.DisableResources +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ Disables embedding resources, essentially meaning no pictures. You can + still link to them though. See %URI.DisableExternalResources for why + this might be a good idea. +

+

+ Note: While this directive has been available since 1.3.0, + it didn't actually start doing anything until 4.2.0. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt new file mode 100644 index 00000000000..ee83b121dec --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt @@ -0,0 +1,19 @@ +URI.Host +TYPE: string/null +VERSION: 1.2.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Defines the domain name of the server, so we can determine whether or + an absolute URI is from your website or not. Not strictly necessary, + as users should be using relative URIs to reference resources on your + website. It will, however, let you use absolute URIs to link to + subdomains of the domain you post here: i.e. example.com will allow + sub.example.com. However, higher up domains will still be excluded: + if you set %URI.Host to sub.example.com, example.com will be blocked. + Note: This directive overrides %URI.Base because + a given page may be on a sub-domain, but you wish HTML Purifier to be + more relaxed and allow some of the parent domains too. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt new file mode 100644 index 00000000000..0b6df7625dc --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt @@ -0,0 +1,9 @@ +URI.HostBlacklist +TYPE: list +VERSION: 1.3.0 +DEFAULT: array() +--DESCRIPTION-- +List of strings that are forbidden in the host of any URI. Use it to kill +domain names of spam, etc. Note that it will catch anything in the domain, +so moo.com will catch moo.com.example.com. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt new file mode 100644 index 00000000000..4214900a592 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt @@ -0,0 +1,13 @@ +URI.MakeAbsolute +TYPE: bool +VERSION: 2.1.0 +DEFAULT: false +--DESCRIPTION-- + +

+ Converts all URIs into absolute forms. This is useful when the HTML + being filtered assumes a specific base path, but will actually be + viewed in a different context (and setting an alternate base URI is + not possible). %URI.Base must be set for this directive to work. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt new file mode 100644 index 00000000000..58c81dcc441 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt @@ -0,0 +1,83 @@ +URI.Munge +TYPE: string/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Munges all browsable (usually http, https and ftp) + absolute URIs into another URI, usually a URI redirection service. + This directive accepts a URI, formatted with a %s where + the url-encoded original URI should be inserted (sample: + http://www.google.com/url?q=%s). +

+

+ Uses for this directive: +

+
    +
  • + Prevent PageRank leaks, while being fairly transparent + to users (you may also want to add some client side JavaScript to + override the text in the statusbar). Notice: + Many security experts believe that this form of protection does not deter spam-bots. +
  • +
  • + Redirect users to a splash page telling them they are leaving your + website. While this is poor usability practice, it is often mandated + in corporate environments. +
  • +
+

+ Prior to HTML Purifier 3.1.1, this directive also enabled the munging + of browsable external resources, which could break things if your redirection + script was a splash page or used meta tags. To revert to + previous behavior, please use %URI.MungeResources. +

+

+ You may want to also use %URI.MungeSecretKey along with this directive + in order to enforce what URIs your redirector script allows. Open + redirector scripts can be a security risk and negatively affect the + reputation of your domain name. +

+

+ Starting with HTML Purifier 3.1.1, there is also these substitutions: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescriptionExample <a href="">
%r1 - The URI embeds a resource
(blank) - The URI is merely a link
%nThe name of the tag this URI came froma
%mThe name of the attribute this URI came fromhref
%pThe name of the CSS property this URI came from, or blank if irrelevant
+

+ Admittedly, these letters are somewhat arbitrary; the only stipulation + was that they couldn't be a through f. r is for resource (I would have preferred + e, but you take what you can get), n is for name, m + was picked because it came after n (and I couldn't use a), p is for + property. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt new file mode 100644 index 00000000000..6fce0fdc373 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt @@ -0,0 +1,17 @@ +URI.MungeResources +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ If true, any URI munging directives like %URI.Munge + will also apply to embedded resources, such as <img src="">. + Be careful enabling this directive if you have a redirector script + that does not use the Location HTTP header; all of your images + and other embedded resources will break. +

+

+ Warning: It is strongly advised you use this in conjunction + %URI.MungeSecretKey to mitigate the security risk of an open redirector. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt new file mode 100644 index 00000000000..1e17c1d461c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt @@ -0,0 +1,30 @@ +URI.MungeSecretKey +TYPE: string/null +VERSION: 3.1.1 +DEFAULT: NULL +--DESCRIPTION-- +

+ This directive enables secure checksum generation along with %URI.Munge. + It should be set to a secure key that is not shared with anyone else. + The checksum can be placed in the URI using %t. Use of this checksum + affords an additional level of protection by allowing a redirector + to check if a URI has passed through HTML Purifier with this line: +

+ +
$checksum === hash_hmac("sha256", $url, $secret_key)
+ +

+ If the output is TRUE, the redirector script should accept the URI. +

+ +

+ Please note that it would still be possible for an attacker to procure + secure hashes en-mass by abusing your website's Preview feature or the + like, but this service affords an additional level of protection + that should be combined with website blacklisting. +

+ +

+ Remember this has no effect if %URI.Munge is not on. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt new file mode 100644 index 00000000000..23331a4e790 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt @@ -0,0 +1,9 @@ +URI.OverrideAllowedSchemes +TYPE: bool +DEFAULT: true +--DESCRIPTION-- +If this is set to true (which it is by default), you can override +%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the +registry. If false, you will also have to update that directive in order +to add more schemes. +--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt new file mode 100644 index 00000000000..79084832be3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt @@ -0,0 +1,22 @@ +URI.SafeIframeRegexp +TYPE: string/null +VERSION: 4.4.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ A PCRE regular expression that will be matched against an iframe URI. This is + a relatively inflexible scheme, but works well enough for the most common + use-case of iframes: embedded video. This directive only has an effect if + %HTML.SafeIframe is enabled. Here are some example values: +

+
    +
  • %^http://www.youtube.com/embed/% - Allow YouTube videos
  • +
  • %^http://player.vimeo.com/video/% - Allow Vimeo videos
  • +
  • %^http://(www.youtube.com/embed/|player.vimeo.com/video/)% - Allow both
  • +
+

+ Note that this directive does not give you enough granularity to, say, disable + all autoplay videos. Pipe up on the HTML Purifier forums if this + is a capability you want. +

+--# vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini new file mode 100644 index 00000000000..5de4505e1b0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini @@ -0,0 +1,3 @@ +name = "HTML Purifier" + +; vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php new file mode 100644 index 00000000000..543e3f8f11f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php @@ -0,0 +1,170 @@ + true) indexed by name. + * @type array + * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets + */ + public $lookup = array(); + + /** + * Synchronized list of defined content sets (keys of info). + * @type array + */ + protected $keys = array(); + /** + * Synchronized list of defined content values (values of info). + * @type array + */ + protected $values = array(); + + /** + * Merges in module's content sets, expands identifiers in the content + * sets and populates the keys, values and lookup member variables. + * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule + */ + public function __construct($modules) + { + if (!is_array($modules)) { + $modules = array($modules); + } + // populate content_sets based on module hints + // sorry, no way of overloading + foreach ($modules as $module) { + foreach ($module->content_sets as $key => $value) { + $temp = $this->convertToLookup($value); + if (isset($this->lookup[$key])) { + // add it into the existing content set + $this->lookup[$key] = array_merge($this->lookup[$key], $temp); + } else { + $this->lookup[$key] = $temp; + } + } + } + $old_lookup = false; + while ($old_lookup !== $this->lookup) { + $old_lookup = $this->lookup; + foreach ($this->lookup as $i => $set) { + $add = array(); + foreach ($set as $element => $x) { + if (isset($this->lookup[$element])) { + $add += $this->lookup[$element]; + unset($this->lookup[$i][$element]); + } + } + $this->lookup[$i] += $add; + } + } + + foreach ($this->lookup as $key => $lookup) { + $this->info[$key] = implode(' | ', array_keys($lookup)); + } + $this->keys = array_keys($this->info); + $this->values = array_values($this->info); + } + + /** + * Accepts a definition; generates and assigns a ChildDef for it + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef + */ + public function generateChildDef(&$def, $module) + { + if (!empty($def->child)) { // already done! + return; + } + $content_model = $def->content_model; + if (is_string($content_model)) { + // Assume that $this->keys is alphanumeric + $def->content_model = preg_replace_callback( + '/\b(' . implode('|', $this->keys) . ')\b/', + array($this, 'generateChildDefCallback'), + $content_model + ); + //$def->content_model = str_replace( + // $this->keys, $this->values, $content_model); + } + $def->child = $this->getChildDef($def, $module); + } + + public function generateChildDefCallback($matches) + { + return $this->info[$matches[0]]; + } + + /** + * Instantiates a ChildDef based on content_model and content_model_type + * member variables in HTMLPurifier_ElementDef + * @note This will also defer to modules for custom HTMLPurifier_ChildDef + * subclasses that need content set expansion + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef + * @return HTMLPurifier_ChildDef corresponding to ElementDef + */ + public function getChildDef($def, $module) + { + $value = $def->content_model; + if (is_object($value)) { + trigger_error( + 'Literal object child definitions should be stored in '. + 'ElementDef->child not ElementDef->content_model', + E_USER_NOTICE + ); + return $value; + } + switch ($def->content_model_type) { + case 'required': + return new HTMLPurifier_ChildDef_Required($value); + case 'optional': + return new HTMLPurifier_ChildDef_Optional($value); + case 'empty': + return new HTMLPurifier_ChildDef_Empty(); + case 'custom': + return new HTMLPurifier_ChildDef_Custom($value); + } + // defer to its module + $return = false; + if ($module->defines_child_def) { // save a func call + $return = $module->getChildDef($def); + } + if ($return !== false) { + return $return; + } + // error-out + trigger_error( + 'Could not determine which ChildDef class to instantiate', + E_USER_ERROR + ); + return false; + } + + /** + * Converts a string list of elements separated by pipes into + * a lookup array. + * @param string $string List of elements + * @return array Lookup array of elements + */ + protected function convertToLookup($string) + { + $array = explode('|', str_replace(' ', '', $string)); + $ret = array(); + foreach ($array as $k) { + $ret[$k] = true; + } + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php new file mode 100644 index 00000000000..00e509c85c6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php @@ -0,0 +1,95 @@ +_storage)) { + trigger_error( + "Name $name produces collision, cannot re-register", + E_USER_ERROR + ); + return; + } + $this->_storage[$name] =& $ref; + } + + /** + * Retrieves a variable reference from the context. + * @param string $name String name + * @param bool $ignore_error Boolean whether or not to ignore error + * @return mixed + */ + public function &get($name, $ignore_error = false) + { + if (!array_key_exists($name, $this->_storage)) { + if (!$ignore_error) { + trigger_error( + "Attempted to retrieve non-existent variable $name", + E_USER_ERROR + ); + } + $var = null; // so we can return by reference + return $var; + } + return $this->_storage[$name]; + } + + /** + * Destroys a variable in the context. + * @param string $name String name + */ + public function destroy($name) + { + if (!array_key_exists($name, $this->_storage)) { + trigger_error( + "Attempted to destroy non-existent variable $name", + E_USER_ERROR + ); + return; + } + unset($this->_storage[$name]); + } + + /** + * Checks whether or not the variable exists. + * @param string $name String name + * @return bool + */ + public function exists($name) + { + return array_key_exists($name, $this->_storage); + } + + /** + * Loads a series of variables from an associative array + * @param array $context_array Assoc array of variables to load + */ + public function loadArray($context_array) + { + foreach ($context_array as $key => $discard) { + $this->register($key, $context_array[$key]); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php new file mode 100644 index 00000000000..bc6d4336477 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php @@ -0,0 +1,55 @@ +setup) { + return; + } + $this->setup = true; + $this->doSetup($config); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php new file mode 100644 index 00000000000..9aa8ff354ff --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php @@ -0,0 +1,129 @@ +type = $type; + } + + /** + * Generates a unique identifier for a particular configuration + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config + * @return string + */ + public function generateKey($config) + { + return $config->version . ',' . // possibly replace with function calls + $config->getBatchSerial($this->type) . ',' . + $config->get($this->type . '.DefinitionRev'); + } + + /** + * Tests whether or not a key is old with respect to the configuration's + * version and revision number. + * @param string $key Key to test + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against + * @return bool + */ + public function isOld($key, $config) + { + if (substr_count($key, ',') < 2) { + return true; + } + list($version, $hash, $revision) = explode(',', $key, 3); + $compare = version_compare($version, $config->version); + // version mismatch, is always old + if ($compare != 0) { + return true; + } + // versions match, ids match, check revision number + if ($hash == $config->getBatchSerial($this->type) && + $revision < $config->get($this->type . '.DefinitionRev')) { + return true; + } + return false; + } + + /** + * Checks if a definition's type jives with the cache's type + * @note Throws an error on failure + * @param HTMLPurifier_Definition $def Definition object to check + * @return bool true if good, false if not + */ + public function checkDefType($def) + { + if ($def->type !== $this->type) { + trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}"); + return false; + } + return true; + } + + /** + * Adds a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function add($def, $config); + + /** + * Unconditionally saves a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function set($def, $config); + + /** + * Replace an object in the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function replace($def, $config); + + /** + * Retrieves a definition object from the cache + * @param HTMLPurifier_Config $config + */ + abstract public function get($config); + + /** + * Removes a definition object to the cache + * @param HTMLPurifier_Config $config + */ + abstract public function remove($config); + + /** + * Clears all objects from cache + * @param HTMLPurifier_Config $config + */ + abstract public function flush($config); + + /** + * Clears all expired (older version or revision) objects from cache + * @note Be careful implementing this method as flush. Flush must + * not interfere with other Definition types, and cleanup() + * should not be repeatedly called by userland code. + * @param HTMLPurifier_Config $config + */ + abstract public function cleanup($config); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php new file mode 100644 index 00000000000..b57a51b6cb8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php @@ -0,0 +1,112 @@ +copy(); + // reference is necessary for mocks in PHP 4 + $decorator->cache =& $cache; + $decorator->type = $cache->type; + return $decorator; + } + + /** + * Cross-compatible clone substitute + * @return HTMLPurifier_DefinitionCache_Decorator + */ + public function copy() + { + return new HTMLPurifier_DefinitionCache_Decorator(); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function add($def, $config) + { + return $this->cache->add($def, $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function set($def, $config) + { + return $this->cache->set($def, $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function replace($def, $config) + { + return $this->cache->replace($def, $config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function get($config) + { + return $this->cache->get($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function remove($config) + { + return $this->cache->remove($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function flush($config) + { + return $this->cache->flush($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function cleanup($config) + { + return $this->cache->cleanup($config); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php new file mode 100644 index 00000000000..4991777ce1a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php @@ -0,0 +1,78 @@ +definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function set($def, $config) + { + $status = parent::set($def, $config); + if ($status) { + $this->definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function replace($def, $config) + { + $status = parent::replace($def, $config); + if ($status) { + $this->definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function get($config) + { + $key = $this->generateKey($config); + if (isset($this->definitions[$key])) { + return $this->definitions[$key]; + } + $this->definitions[$key] = parent::get($config); + return $this->definitions[$key]; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in new file mode 100644 index 00000000000..b1fec8d3673 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in @@ -0,0 +1,82 @@ +checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (file_exists($file)) { + return false; + } + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return int|bool + */ + public function set($def, $config) + { + if (!$this->checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return int|bool + */ + public function replace($def, $config) + { + if (!$this->checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool|HTMLPurifier_Config + */ + public function get($config) + { + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + return unserialize(file_get_contents($file)); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function remove($config) + { + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + return unlink($file); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function flush($config) + { + if (!$this->_prepareDir($config)) { + return false; + } + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + // Apparently, on some versions of PHP, readdir will return + // an empty string if you pass an invalid argument to readdir. + // So you need this test. See #49. + if (false === $dh) { + return false; + } + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) { + continue; + } + if ($filename[0] === '.') { + continue; + } + unlink($dir . '/' . $filename); + } + closedir($dh); + return true; + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function cleanup($config) + { + if (!$this->_prepareDir($config)) { + return false; + } + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + // See #49 (and above). + if (false === $dh) { + return false; + } + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) { + continue; + } + if ($filename[0] === '.') { + continue; + } + $key = substr($filename, 0, strlen($filename) - 4); + if ($this->isOld($key, $config)) { + unlink($dir . '/' . $filename); + } + } + closedir($dh); + return true; + } + + /** + * Generates the file path to the serial file corresponding to + * the configuration and definition name + * @param HTMLPurifier_Config $config + * @return string + * @todo Make protected + */ + public function generateFilePath($config) + { + $key = $this->generateKey($config); + return $this->generateDirectoryPath($config) . '/' . $key . '.ser'; + } + + /** + * Generates the path to the directory contain this cache's serial files + * @param HTMLPurifier_Config $config + * @return string + * @note No trailing slash + * @todo Make protected + */ + public function generateDirectoryPath($config) + { + $base = $this->generateBaseDirectoryPath($config); + return $base . '/' . $this->type; + } + + /** + * Generates path to base directory that contains all definition type + * serials + * @param HTMLPurifier_Config $config + * @return mixed|string + * @todo Make protected + */ + public function generateBaseDirectoryPath($config) + { + $base = $config->get('Cache.SerializerPath'); + $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base; + return $base; + } + + /** + * Convenience wrapper function for file_put_contents + * @param string $file File name to write to + * @param string $data Data to write into file + * @param HTMLPurifier_Config $config + * @return int|bool Number of bytes written if success, or false if failure. + */ + private function _write($file, $data, $config) + { + $result = file_put_contents($file, $data); + if ($result !== false) { + // set permissions of the new file (no execute) + $chmod = $config->get('Cache.SerializerPermissions'); + if ($chmod !== null) { + chmod($file, $chmod & 0666); + } + } + return $result; + } + + /** + * Prepares the directory that this type stores the serials in + * @param HTMLPurifier_Config $config + * @return bool True if successful + */ + private function _prepareDir($config) + { + $directory = $this->generateDirectoryPath($config); + $chmod = $config->get('Cache.SerializerPermissions'); + if ($chmod === null) { + if (!@mkdir($directory) && !is_dir($directory)) { + trigger_error( + 'Could not create directory ' . $directory . '', + E_USER_WARNING + ); + return false; + } + return true; + } + if (!is_dir($directory)) { + $base = $this->generateBaseDirectoryPath($config); + if (!is_dir($base)) { + trigger_error( + 'Base directory ' . $base . ' does not exist, + please create or change using %Cache.SerializerPath', + E_USER_WARNING + ); + return false; + } elseif (!$this->_testPermissions($base, $chmod)) { + return false; + } + if (!@mkdir($directory, $chmod) && !is_dir($directory)) { + trigger_error( + 'Could not create directory ' . $directory . '', + E_USER_WARNING + ); + return false; + } + if (!$this->_testPermissions($directory, $chmod)) { + return false; + } + } elseif (!$this->_testPermissions($directory, $chmod)) { + return false; + } + return true; + } + + /** + * Tests permissions on a directory and throws out friendly + * error messages and attempts to chmod it itself if possible + * @param string $dir Directory path + * @param int $chmod Permissions + * @return bool True if directory is writable + */ + private function _testPermissions($dir, $chmod) + { + // early abort, if it is writable, everything is hunky-dory + if (is_writable($dir)) { + return true; + } + if (!is_dir($dir)) { + // generally, you'll want to handle this beforehand + // so a more specific error message can be given + trigger_error( + 'Directory ' . $dir . ' does not exist', + E_USER_WARNING + ); + return false; + } + if (function_exists('posix_getuid') && $chmod !== null) { + // POSIX system, we can give more specific advice + if (fileowner($dir) === posix_getuid()) { + // we can chmod it ourselves + $chmod = $chmod | 0700; + if (chmod($dir, $chmod)) { + return true; + } + } elseif (filegroup($dir) === posix_getgid()) { + $chmod = $chmod | 0070; + } else { + // PHP's probably running as nobody, so we'll + // need to give global permissions + $chmod = $chmod | 0777; + } + trigger_error( + 'Directory ' . $dir . ' not writable, ' . + 'please chmod to ' . decoct($chmod), + E_USER_WARNING + ); + } else { + // generic error message + trigger_error( + 'Directory ' . $dir . ' not writable, ' . + 'please alter file permissions', + E_USER_WARNING + ); + } + return false; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README new file mode 100644 index 00000000000..2e35c1c3d06 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README @@ -0,0 +1,3 @@ +This is a dummy file to prevent Git from ignoring this empty directory. + + vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php new file mode 100644 index 00000000000..fd1cc9be46a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php @@ -0,0 +1,106 @@ + array()); + + /** + * @type array + */ + protected $implementations = array(); + + /** + * @type HTMLPurifier_DefinitionCache_Decorator[] + */ + protected $decorators = array(); + + /** + * Initialize default decorators + */ + public function setup() + { + $this->addDecorator('Cleanup'); + } + + /** + * Retrieves an instance of global definition cache factory. + * @param HTMLPurifier_DefinitionCacheFactory $prototype + * @return HTMLPurifier_DefinitionCacheFactory + */ + public static function instance($prototype = null) + { + static $instance; + if ($prototype !== null) { + $instance = $prototype; + } elseif ($instance === null || $prototype === true) { + $instance = new HTMLPurifier_DefinitionCacheFactory(); + $instance->setup(); + } + return $instance; + } + + /** + * Registers a new definition cache object + * @param string $short Short name of cache object, for reference + * @param string $long Full class name of cache object, for construction + */ + public function register($short, $long) + { + $this->implementations[$short] = $long; + } + + /** + * Factory method that creates a cache object based on configuration + * @param string $type Name of definitions handled by cache + * @param HTMLPurifier_Config $config Config instance + * @return mixed + */ + public function create($type, $config) + { + $method = $config->get('Cache.DefinitionImpl'); + if ($method === null) { + return new HTMLPurifier_DefinitionCache_Null($type); + } + if (!empty($this->caches[$method][$type])) { + return $this->caches[$method][$type]; + } + if (isset($this->implementations[$method]) && + class_exists($class = $this->implementations[$method], false)) { + $cache = new $class($type); + } else { + if ($method != 'Serializer') { + trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING); + } + $cache = new HTMLPurifier_DefinitionCache_Serializer($type); + } + foreach ($this->decorators as $decorator) { + $new_cache = $decorator->decorate($cache); + // prevent infinite recursion in PHP 4 + unset($cache); + $cache = $new_cache; + } + $this->caches[$method][$type] = $cache; + return $this->caches[$method][$type]; + } + + /** + * Registers a decorator to add to all new cache objects + * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator + */ + public function addDecorator($decorator) + { + if (is_string($decorator)) { + $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator"; + $decorator = new $class; + } + $this->decorators[$decorator->name] = $decorator; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php new file mode 100644 index 00000000000..4acd06e5bdd --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php @@ -0,0 +1,73 @@ +renderDoctype. + * If structure changes, please update that function. + */ +class HTMLPurifier_Doctype +{ + /** + * Full name of doctype + * @type string + */ + public $name; + + /** + * List of standard modules (string identifiers or literal objects) + * that this doctype uses + * @type array + */ + public $modules = array(); + + /** + * List of modules to use for tidying up code + * @type array + */ + public $tidyModules = array(); + + /** + * Is the language derived from XML (i.e. XHTML)? + * @type bool + */ + public $xml = true; + + /** + * List of aliases for this doctype + * @type array + */ + public $aliases = array(); + + /** + * Public DTD identifier + * @type string + */ + public $dtdPublic; + + /** + * System DTD identifier + * @type string + */ + public $dtdSystem; + + public function __construct( + $name = null, + $xml = true, + $modules = array(), + $tidyModules = array(), + $aliases = array(), + $dtd_public = null, + $dtd_system = null + ) { + $this->name = $name; + $this->xml = $xml; + $this->modules = $modules; + $this->tidyModules = $tidyModules; + $this->aliases = $aliases; + $this->dtdPublic = $dtd_public; + $this->dtdSystem = $dtd_system; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php new file mode 100644 index 00000000000..acc1d64a625 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php @@ -0,0 +1,142 @@ +doctypes[$doctype->name] = $doctype; + $name = $doctype->name; + // hookup aliases + foreach ($doctype->aliases as $alias) { + if (isset($this->doctypes[$alias])) { + continue; + } + $this->aliases[$alias] = $name; + } + // remove old aliases + if (isset($this->aliases[$name])) { + unset($this->aliases[$name]); + } + return $doctype; + } + + /** + * Retrieves reference to a doctype of a certain name + * @note This function resolves aliases + * @note When possible, use the more fully-featured make() + * @param string $doctype Name of doctype + * @return HTMLPurifier_Doctype Editable doctype object + */ + public function get($doctype) + { + if (isset($this->aliases[$doctype])) { + $doctype = $this->aliases[$doctype]; + } + if (!isset($this->doctypes[$doctype])) { + trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); + $anon = new HTMLPurifier_Doctype($doctype); + return $anon; + } + return $this->doctypes[$doctype]; + } + + /** + * Creates a doctype based on a configuration object, + * will perform initialization on the doctype + * @note Use this function to get a copy of doctype that config + * can hold on to (this is necessary in order to tell + * Generator whether or not the current document is XML + * based or not). + * @param HTMLPurifier_Config $config + * @return HTMLPurifier_Doctype + */ + public function make($config) + { + return clone $this->get($this->getDoctypeFromConfig($config)); + } + + /** + * Retrieves the doctype from the configuration object + * @param HTMLPurifier_Config $config + * @return string + */ + public function getDoctypeFromConfig($config) + { + // recommended test + $doctype = $config->get('HTML.Doctype'); + if (!empty($doctype)) { + return $doctype; + } + $doctype = $config->get('HTML.CustomDoctype'); + if (!empty($doctype)) { + return $doctype; + } + // backwards-compatibility + if ($config->get('HTML.XHTML')) { + $doctype = 'XHTML 1.0'; + } else { + $doctype = 'HTML 4.01'; + } + if ($config->get('HTML.Strict')) { + $doctype .= ' Strict'; + } else { + $doctype .= ' Transitional'; + } + return $doctype; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php new file mode 100644 index 00000000000..d5311cedcf3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php @@ -0,0 +1,216 @@ +setup(), this array may also + * contain an array at index 0 that indicates which attribute + * collections to load into the full array. It may also + * contain string indentifiers in lieu of HTMLPurifier_AttrDef, + * see HTMLPurifier_AttrTypes on how they are expanded during + * HTMLPurifier_HTMLDefinition->setup() processing. + */ + public $attr = array(); + + // XXX: Design note: currently, it's not possible to override + // previously defined AttrTransforms without messing around with + // the final generated config. This is by design; a previous version + // used an associated list of attr_transform, but it was extremely + // easy to accidentally override other attribute transforms by + // forgetting to specify an index (and just using 0.) While we + // could check this by checking the index number and complaining, + // there is a second problem which is that it is not at all easy to + // tell when something is getting overridden. Combine this with a + // codebase where this isn't really being used, and it's perfect for + // nuking. + + /** + * List of tags HTMLPurifier_AttrTransform to be done before validation. + * @type array + */ + public $attr_transform_pre = array(); + + /** + * List of tags HTMLPurifier_AttrTransform to be done after validation. + * @type array + */ + public $attr_transform_post = array(); + + /** + * HTMLPurifier_ChildDef of this tag. + * @type HTMLPurifier_ChildDef + */ + public $child; + + /** + * Abstract string representation of internal ChildDef rules. + * @see HTMLPurifier_ContentSets for how this is parsed and then transformed + * into an HTMLPurifier_ChildDef. + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + * @type string + */ + public $content_model; + + /** + * Value of $child->type, used to determine which ChildDef to use, + * used in combination with $content_model. + * @warning This must be lowercase + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + * @type string + */ + public $content_model_type; + + /** + * Does the element have a content model (#PCDATA | Inline)*? This + * is important for chameleon ins and del processing in + * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't + * have to worry about this one. + * @type bool + */ + public $descendants_are_inline = false; + + /** + * List of the names of required attributes this element has. + * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement() + * @type array + */ + public $required_attr = array(); + + /** + * Lookup table of tags excluded from all descendants of this tag. + * @type array + * @note SGML permits exclusions for all descendants, but this is + * not possible with DTDs or XML Schemas. W3C has elected to + * use complicated compositions of content_models to simulate + * exclusion for children, but we go the simpler, SGML-style + * route of flat-out exclusions, which correctly apply to + * all descendants and not just children. Note that the XHTML + * Modularization Abstract Modules are blithely unaware of such + * distinctions. + */ + public $excludes = array(); + + /** + * This tag is explicitly auto-closed by the following tags. + * @type array + */ + public $autoclose = array(); + + /** + * If a foreign element is found in this element, test if it is + * allowed by this sub-element; if it is, instead of closing the + * current element, place it inside this element. + * @type string + */ + public $wrap; + + /** + * Whether or not this is a formatting element affected by the + * "Active Formatting Elements" algorithm. + * @type bool + */ + public $formatting; + + /** + * Low-level factory constructor for creating new standalone element defs + */ + public static function create($content_model, $content_model_type, $attr) + { + $def = new HTMLPurifier_ElementDef(); + $def->content_model = $content_model; + $def->content_model_type = $content_model_type; + $def->attr = $attr; + return $def; + } + + /** + * Merges the values of another element definition into this one. + * Values from the new element def take precedence if a value is + * not mergeable. + * @param HTMLPurifier_ElementDef $def + */ + public function mergeIn($def) + { + // later keys takes precedence + foreach ($def->attr as $k => $v) { + if ($k === 0) { + // merge in the includes + // sorry, no way to override an include + foreach ($v as $v2) { + $this->attr[0][] = $v2; + } + continue; + } + if ($v === false) { + if (isset($this->attr[$k])) { + unset($this->attr[$k]); + } + continue; + } + $this->attr[$k] = $v; + } + $this->_mergeAssocArray($this->excludes, $def->excludes); + $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre); + $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post); + + if (!empty($def->content_model)) { + $this->content_model = + str_replace("#SUPER", $this->content_model, $def->content_model); + $this->child = false; + } + if (!empty($def->content_model_type)) { + $this->content_model_type = $def->content_model_type; + $this->child = false; + } + if (!is_null($def->child)) { + $this->child = $def->child; + } + if (!is_null($def->formatting)) { + $this->formatting = $def->formatting; + } + if ($def->descendants_are_inline) { + $this->descendants_are_inline = $def->descendants_are_inline; + } + } + + /** + * Merges one array into another, removes values which equal false + * @param $a1 Array by reference that is merged into + * @param $a2 Array that merges into $a1 + */ + private function _mergeAssocArray(&$a1, $a2) + { + foreach ($a2 as $k => $v) { + if ($v === false) { + if (isset($a1[$k])) { + unset($a1[$k]); + } + continue; + } + $a1[$k] = $v; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php new file mode 100644 index 00000000000..40a24266a46 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php @@ -0,0 +1,617 @@ += $c) { + $r .= self::unsafeIconv($in, $out, substr($text, $i)); + break; + } + // wibble the boundary + if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) { + $chunk_size = $max_chunk_size; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) { + $chunk_size = $max_chunk_size - 1; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) { + $chunk_size = $max_chunk_size - 2; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) { + $chunk_size = $max_chunk_size - 3; + } else { + return false; // rather confusing UTF-8... + } + $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths + $r .= self::unsafeIconv($in, $out, $chunk); + $i += $chunk_size; + } + return $r; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Cleans a UTF-8 string for well-formedness and SGML validity + * + * It will parse according to UTF-8 and return a valid UTF8 string, with + * non-SGML codepoints excluded. + * + * Specifically, it will permit: + * \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF} + * Source: https://www.w3.org/TR/REC-xml/#NT-Char + * Arguably this function should be modernized to the HTML5 set + * of allowed characters: + * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream + * which simultaneously expand and restrict the set of allowed characters. + * + * @param string $str The string to clean + * @param bool $force_php + * @return string + * + * @note Just for reference, the non-SGML code points are 0 to 31 and + * 127 to 159, inclusive. However, we allow code points 9, 10 + * and 13, which are the tab, line feed and carriage return + * respectively. 128 and above the code points map to multibyte + * UTF-8 representations. + * + * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and + * hsivonen@iki.fi at under the + * LGPL license. Notes on what changed are inside, but in general, + * the original code transformed UTF-8 text into an array of integer + * Unicode codepoints. Understandably, transforming that back to + * a string would be somewhat expensive, so the function was modded to + * directly operate on the string. However, this discourages code + * reuse, and the logic enumerated here would be useful for any + * function that needs to be able to understand UTF-8 characters. + * As of right now, only smart lossless character encoding converters + * would need that, and I'm probably not going to implement them. + */ + public static function cleanUTF8($str, $force_php = false) + { + // UTF-8 validity is checked since PHP 4.3.5 + // This is an optimization: if the string is already valid UTF-8, no + // need to do PHP stuff. 99% of the time, this will be the case. + if (preg_match( + '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', + $str + )) { + return $str; + } + + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + // original code involved an $out that was an array of Unicode + // codepoints. Instead of having to convert back into UTF-8, we've + // decided to directly append valid UTF-8 characters onto a string + // $out once they're done. $char accumulates raw bytes, while $mUcs4 + // turns into the Unicode code point, so there's some redundancy. + + $out = ''; + $char = ''; + + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $in = ord($str[$i]); + $char .= $str[$i]; // append byte to char + if (0 == $mState) { + // When mState is zero we expect either a US-ASCII character + // or a multi-octet sequence. + if (0 == (0x80 & ($in))) { + // US-ASCII, pass straight through. + if (($in <= 31 || $in == 127) && + !($in == 9 || $in == 13 || $in == 10) // save \r\t\n + ) { + // control characters, remove + } else { + $out .= $char; + } + // reset + $char = ''; + $mBytes = 1; + } elseif (0xC0 == (0xE0 & ($in))) { + // First octet of 2 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + } elseif (0xE0 == (0xF0 & ($in))) { + // First octet of 3 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + } elseif (0xF0 == (0xF8 & ($in))) { + // First octet of 4 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + } elseif (0xF8 == (0xFC & ($in))) { + // First octet of 5 octet sequence. + // + // This is illegal because the encoded codepoint must be + // either: + // (a) not the shortest form or + // (b) outside the Unicode range of 0-0x10FFFF. + // Rather than trying to resynchronize, we will carry on + // until the end of the sequence and let the later error + // handling code catch it. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + } elseif (0xFC == (0xFE & ($in))) { + // First octet of 6 octet sequence, see comments for 5 + // octet sequence. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + } else { + // Current octet is neither in the US-ASCII range nor a + // legal first octet of a multi-octet sequence. + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // When mState is non-zero, we expect a continuation of the + // multi-octet sequence + if (0x80 == (0xC0 & ($in))) { + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + + if (0 == --$mState) { + // End of the multi-octet sequence. mUcs4 now contains + // the final Unicode codepoint to be output + + // Check for illegal sequences and codepoints. + + // From Unicode 3.1, non-shortest form is illegal + if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || + ((3 == $mBytes) && ($mUcs4 < 0x0800)) || + ((4 == $mBytes) && ($mUcs4 < 0x10000)) || + (4 < $mBytes) || + // From Unicode 3.2, surrogate characters = illegal + (($mUcs4 & 0xFFFFF800) == 0xD800) || + // Codepoints outside the Unicode range are illegal + ($mUcs4 > 0x10FFFF) + ) { + + } elseif (0xFEFF != $mUcs4 && // omit BOM + // check for valid Char unicode codepoints + ( + 0x9 == $mUcs4 || + 0xA == $mUcs4 || + 0xD == $mUcs4 || + (0x20 <= $mUcs4 && 0x7E >= $mUcs4) || + // 7F-9F is not strictly prohibited by XML, + // but it is non-SGML, and thus we don't allow it + (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) || + (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) || + (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4) + ) + ) { + $out .= $char; + } + // initialize UTF8 cache (reset) + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // ((0xC0 & (*in) != 0x80) && (mState != 0)) + // Incomplete multi-octet sequence. + // used to result in complete fail, but we'll reset + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char =''; + } + } + } + return $out; + } + + /** + * Translates a Unicode codepoint into its corresponding UTF-8 character. + * @note Based on Feyd's function at + * , + * which is in public domain. + * @note While we're going to do code point parsing anyway, a good + * optimization would be to refuse to translate code points that + * are non-SGML characters. However, this could lead to duplication. + * @note This is very similar to the unichr function in + * maintenance/generate-entity-file.php (although this is superior, + * due to its sanity checks). + */ + + // +----------+----------+----------+----------+ + // | 33222222 | 22221111 | 111111 | | + // | 10987654 | 32109876 | 54321098 | 76543210 | bit + // +----------+----------+----------+----------+ + // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F + // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF + // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF + // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF + // +----------+----------+----------+----------+ + // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF) + // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes + // +----------+----------+----------+----------+ + + public static function unichr($code) + { + if ($code > 1114111 or $code < 0 or + ($code >= 55296 and $code <= 57343) ) { + // bits are set outside the "valid" range as defined + // by UNICODE 4.1.0 + return ''; + } + + $x = $y = $z = $w = 0; + if ($code < 128) { + // regular ASCII character + $x = $code; + } else { + // set up bits for UTF-8 + $x = ($code & 63) | 128; + if ($code < 2048) { + $y = (($code & 2047) >> 6) | 192; + } else { + $y = (($code & 4032) >> 6) | 128; + if ($code < 65536) { + $z = (($code >> 12) & 15) | 224; + } else { + $z = (($code >> 12) & 63) | 128; + $w = (($code >> 18) & 7) | 240; + } + } + } + // set up the actual character + $ret = ''; + if ($w) { + $ret .= chr($w); + } + if ($z) { + $ret .= chr($z); + } + if ($y) { + $ret .= chr($y); + } + $ret .= chr($x); + + return $ret; + } + + /** + * @return bool + */ + public static function iconvAvailable() + { + static $iconv = null; + if ($iconv === null) { + $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE; + } + return $iconv; + } + + /** + * Convert a string to UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public static function convertToUTF8($str, $config, $context) + { + $encoding = $config->get('Core.Encoding'); + if ($encoding === 'utf-8') { + return $str; + } + static $iconv = null; + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } + if ($iconv && !$config->get('Test.ForceNoIconv')) { + // unaffected by bugs, since UTF-8 support all characters + $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str); + if ($str === false) { + // $encoding is not a valid encoding + trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR); + return ''; + } + // If the string is bjorked by Shift_JIS or a similar encoding + // that doesn't support all of ASCII, convert the naughty + // characters to their true byte-wise ASCII/UTF-8 equivalents. + $str = strtr($str, self::testEncodingSupportsASCII($encoding)); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_encode($str); + return $str; + } + $bug = HTMLPurifier_Encoder::testIconvTruncateBug(); + if ($bug == self::ICONV_OK) { + trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); + } else { + trigger_error( + 'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' . + 'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541', + E_USER_ERROR + ); + } + } + + /** + * Converts a string from UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + * @note Currently, this is a lossy conversion, with unexpressable + * characters being omitted. + */ + public static function convertFromUTF8($str, $config, $context) + { + $encoding = $config->get('Core.Encoding'); + if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { + $str = self::convertToASCIIDumbLossless($str); + } + if ($encoding === 'utf-8') { + return $str; + } + static $iconv = null; + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } + if ($iconv && !$config->get('Test.ForceNoIconv')) { + // Undo our previous fix in convertToUTF8, otherwise iconv will barf + $ascii_fix = self::testEncodingSupportsASCII($encoding); + if (!$escape && !empty($ascii_fix)) { + $clear_fix = array(); + foreach ($ascii_fix as $utf8 => $native) { + $clear_fix[$utf8] = ''; + } + $str = strtr($str, $clear_fix); + } + $str = strtr($str, array_flip($ascii_fix)); + // Normal stuff + $str = self::iconv('utf-8', $encoding . '//IGNORE', $str); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_decode($str); + return $str; + } + trigger_error('Encoding not supported', E_USER_ERROR); + // You might be tempted to assume that the ASCII representation + // might be OK, however, this is *not* universally true over all + // encodings. So we take the conservative route here, rather + // than forcibly turn on %Core.EscapeNonASCIICharacters + } + + /** + * Lossless (character-wise) conversion of HTML to ASCII + * @param string $str UTF-8 string to be converted to ASCII + * @return string ASCII encoded string with non-ASCII character entity-ized + * @warning Adapted from MediaWiki, claiming fair use: this is a common + * algorithm. If you disagree with this license fudgery, + * implement it yourself. + * @note Uses decimal numeric entities since they are best supported. + * @note This is a DUMB function: it has no concept of keeping + * character entities that the projected character encoding + * can allow. We could possibly implement a smart version + * but that would require it to also know which Unicode + * codepoints the charset supported (not an easy task). + * @note Sort of with cleanUTF8() but it assumes that $str is + * well-formed UTF-8 + */ + public static function convertToASCIIDumbLossless($str) + { + $bytesleft = 0; + $result = ''; + $working = 0; + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $bytevalue = ord($str[$i]); + if ($bytevalue <= 0x7F) { //0xxx xxxx + $result .= chr($bytevalue); + $bytesleft = 0; + } elseif ($bytevalue <= 0xBF) { //10xx xxxx + $working = $working << 6; + $working += ($bytevalue & 0x3F); + $bytesleft--; + if ($bytesleft <= 0) { + $result .= "&#" . $working . ";"; + } + } elseif ($bytevalue <= 0xDF) { //110x xxxx + $working = $bytevalue & 0x1F; + $bytesleft = 1; + } elseif ($bytevalue <= 0xEF) { //1110 xxxx + $working = $bytevalue & 0x0F; + $bytesleft = 2; + } else { //1111 0xxx + $working = $bytevalue & 0x07; + $bytesleft = 3; + } + } + return $result; + } + + /** No bugs detected in iconv. */ + const ICONV_OK = 0; + + /** Iconv truncates output if converting from UTF-8 to another + * character set with //IGNORE, and a non-encodable character is found */ + const ICONV_TRUNCATES = 1; + + /** Iconv does not support //IGNORE, making it unusable for + * transcoding purposes */ + const ICONV_UNUSABLE = 2; + + /** + * glibc iconv has a known bug where it doesn't handle the magic + * //IGNORE stanza correctly. In particular, rather than ignore + * characters, it will return an EILSEQ after consuming some number + * of characters, and expect you to restart iconv as if it were + * an E2BIG. Old versions of PHP did not respect the errno, and + * returned the fragment, so as a result you would see iconv + * mysteriously truncating output. We can work around this by + * manually chopping our input into segments of about 8000 + * characters, as long as PHP ignores the error code. If PHP starts + * paying attention to the error code, iconv becomes unusable. + * + * @return int Error code indicating severity of bug. + */ + public static function testIconvTruncateBug() + { + static $code = null; + if ($code === null) { + // better not use iconv, otherwise infinite loop! + $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000)); + if ($r === false) { + $code = self::ICONV_UNUSABLE; + } elseif (($c = strlen($r)) < 9000) { + $code = self::ICONV_TRUNCATES; + } elseif ($c > 9000) { + trigger_error( + 'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' . + 'include your iconv version as per phpversion()', + E_USER_ERROR + ); + } else { + $code = self::ICONV_OK; + } + } + return $code; + } + + /** + * This expensive function tests whether or not a given character + * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will + * fail this test, and require special processing. Variable width + * encodings shouldn't ever fail. + * + * @param string $encoding Encoding name to test, as per iconv format + * @param bool $bypass Whether or not to bypass the precompiled arrays. + * @return Array of UTF-8 characters to their corresponding ASCII, + * which can be used to "undo" any overzealous iconv action. + */ + public static function testEncodingSupportsASCII($encoding, $bypass = false) + { + // All calls to iconv here are unsafe, proof by case analysis: + // If ICONV_OK, no difference. + // If ICONV_TRUNCATE, all calls involve one character inputs, + // so bug is not triggered. + // If ICONV_UNUSABLE, this call is irrelevant + static $encodings = array(); + if (!$bypass) { + if (isset($encodings[$encoding])) { + return $encodings[$encoding]; + } + $lenc = strtolower($encoding); + switch ($lenc) { + case 'shift_jis': + return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~'); + case 'johab': + return array("\xE2\x82\xA9" => '\\'); + } + if (strpos($lenc, 'iso-8859-') === 0) { + return array(); + } + } + $ret = array(); + if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) { + return false; + } + for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars + $c = chr($i); // UTF-8 char + $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion + if ($r === '' || + // This line is needed for iconv implementations that do not + // omit characters that do not exist in the target character set + ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c) + ) { + // Reverse engineer: what's the UTF-8 equiv of this byte + // sequence? This assumes that there's no variable width + // encoding that doesn't support ASCII. + $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c; + } + } + $encodings[$encoding] = $ret; + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php new file mode 100644 index 00000000000..f12ff13a35a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php @@ -0,0 +1,48 @@ +table = unserialize(file_get_contents($file)); + } + + /** + * Retrieves sole instance of the object. + * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with. + * @return HTMLPurifier_EntityLookup + */ + public static function instance($prototype = false) + { + // no references, since PHP doesn't copy unless modified + static $instance = null; + if ($prototype) { + $instance = $prototype; + } elseif (!$instance) { + $instance = new HTMLPurifier_EntityLookup(); + $instance->setup(); + } + return $instance; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser new file mode 100644 index 00000000000..e8b08128beb --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser @@ -0,0 +1 @@ +a:253:{s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:6:"there4";s:3:"∴";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:4:"sup2";s:2:"²";s:4:"sup3";s:2:"³";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"sup1";s:2:"¹";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"frac14";s:2:"¼";s:6:"frac12";s:2:"½";s:6:"frac34";s:2:"¾";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";} \ No newline at end of file diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php new file mode 100644 index 00000000000..3ef2d09ec60 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php @@ -0,0 +1,285 @@ +_semiOptionalPrefixRegex = "/&()()()($semi_optional)/"; + + $this->_textEntitiesRegex = + '/&(?:'. + // hex + '[#]x([a-fA-F0-9]+);?|'. + // dec + '[#]0*(\d+);?|'. + // string (mandatory semicolon) + // NB: order matters: match semicolon preferentially + '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'. + // string (optional semicolon) + "($semi_optional)". + ')/'; + + $this->_attrEntitiesRegex = + '/&(?:'. + // hex + '[#]x([a-fA-F0-9]+);?|'. + // dec + '[#]0*(\d+);?|'. + // string (mandatory semicolon) + // NB: order matters: match semicolon preferentially + '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'. + // string (optional semicolon) + // don't match if trailing is equals or alphanumeric (URL + // like) + "($semi_optional)(?![=;A-Za-z0-9])". + ')/'; + + } + + /** + * Substitute entities with the parsed equivalents. Use this on + * textual data in an HTML document (as opposed to attributes.) + * + * @param string $string String to have entities parsed. + * @return string Parsed string. + */ + public function substituteTextEntities($string) + { + return preg_replace_callback( + $this->_textEntitiesRegex, + array($this, 'entityCallback'), + $string + ); + } + + /** + * Substitute entities with the parsed equivalents. Use this on + * attribute contents in documents. + * + * @param string $string String to have entities parsed. + * @return string Parsed string. + */ + public function substituteAttrEntities($string) + { + return preg_replace_callback( + $this->_attrEntitiesRegex, + array($this, 'entityCallback'), + $string + ); + } + + /** + * Callback function for substituteNonSpecialEntities() that does the work. + * + * @param array $matches PCRE matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + + protected function entityCallback($matches) + { + $entity = $matches[0]; + $hex_part = @$matches[1]; + $dec_part = @$matches[2]; + $named_part = empty($matches[3]) ? (empty($matches[4]) ? "" : $matches[4]) : $matches[3]; + if ($hex_part !== NULL && $hex_part !== "") { + return HTMLPurifier_Encoder::unichr(hexdec($hex_part)); + } elseif ($dec_part !== NULL && $dec_part !== "") { + return HTMLPurifier_Encoder::unichr((int) $dec_part); + } else { + if (!$this->_entity_lookup) { + $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); + } + if (isset($this->_entity_lookup->table[$named_part])) { + return $this->_entity_lookup->table[$named_part]; + } else { + // exact match didn't match anything, so test if + // any of the semicolon optional match the prefix. + // Test that this is an EXACT match is important to + // prevent infinite loop + if (!empty($matches[3])) { + return preg_replace_callback( + $this->_semiOptionalPrefixRegex, + array($this, 'entityCallback'), + $entity + ); + } + return $entity; + } + } + } + + // LEGACY CODE BELOW + + /** + * Callback regex string for parsing entities. + * @type string + */ + protected $_substituteEntitiesRegex = + '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/'; + // 1. hex 2. dec 3. string (XML style) + + /** + * Decimal to parsed string conversion table for special entities. + * @type array + */ + protected $_special_dec2str = + array( + 34 => '"', + 38 => '&', + 39 => "'", + 60 => '<', + 62 => '>' + ); + + /** + * Stripped entity names to decimal conversion table for special entities. + * @type array + */ + protected $_special_ent2dec = + array( + 'quot' => 34, + 'amp' => 38, + 'lt' => 60, + 'gt' => 62 + ); + + /** + * Substitutes non-special entities with their parsed equivalents. Since + * running this whenever you have parsed character is t3h 5uck, we run + * it before everything else. + * + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. + */ + public function substituteNonSpecialEntities($string) + { + // it will try to detect missing semicolons, but don't rely on it + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'nonSpecialEntityCallback'), + $string + ); + } + + /** + * Callback function for substituteNonSpecialEntities() that does the work. + * + * @param array $matches PCRE matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + + protected function nonSpecialEntityCallback($matches) + { + // replaces all but big five + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + // abort for special characters + if (isset($this->_special_dec2str[$code])) { + return $entity; + } + return HTMLPurifier_Encoder::unichr($code); + } else { + if (isset($this->_special_ent2dec[$matches[3]])) { + return $entity; + } + if (!$this->_entity_lookup) { + $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); + } + if (isset($this->_entity_lookup->table[$matches[3]])) { + return $this->_entity_lookup->table[$matches[3]]; + } else { + return $entity; + } + } + } + + /** + * Substitutes only special entities with their parsed equivalents. + * + * @notice We try to avoid calling this function because otherwise, it + * would have to be called a lot (for every parsed section). + * + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. + */ + public function substituteSpecialEntities($string) + { + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'specialEntityCallback'), + $string + ); + } + + /** + * Callback function for substituteSpecialEntities() that does the work. + * + * This callback has same syntax as nonSpecialEntityCallback(). + * + * @param array $matches PCRE-style matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + protected function specialEntityCallback($matches) + { + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + return isset($this->_special_dec2str[$int]) ? + $this->_special_dec2str[$int] : + $entity; + } else { + return isset($this->_special_ent2dec[$matches[3]]) ? + $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] : + $entity; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php new file mode 100644 index 00000000000..d47e3f2e24f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php @@ -0,0 +1,244 @@ +locale =& $context->get('Locale'); + $this->context = $context; + $this->_current =& $this->_stacks[0]; + $this->errors =& $this->_stacks[0]; + } + + /** + * Sends an error message to the collector for later use + * @param int $severity Error severity, PHP error style (don't use E_USER_) + * @param string $msg Error message text + */ + public function send($severity, $msg) + { + $args = array(); + if (func_num_args() > 2) { + $args = func_get_args(); + array_shift($args); + unset($args[0]); + } + + $token = $this->context->get('CurrentToken', true); + $line = $token ? $token->line : $this->context->get('CurrentLine', true); + $col = $token ? $token->col : $this->context->get('CurrentCol', true); + $attr = $this->context->get('CurrentAttr', true); + + // perform special substitutions, also add custom parameters + $subst = array(); + if (!is_null($token)) { + $args['CurrentToken'] = $token; + } + if (!is_null($attr)) { + $subst['$CurrentAttr.Name'] = $attr; + if (isset($token->attr[$attr])) { + $subst['$CurrentAttr.Value'] = $token->attr[$attr]; + } + } + + if (empty($args)) { + $msg = $this->locale->getMessage($msg); + } else { + $msg = $this->locale->formatMessage($msg, $args); + } + + if (!empty($subst)) { + $msg = strtr($msg, $subst); + } + + // (numerically indexed) + $error = array( + self::LINENO => $line, + self::SEVERITY => $severity, + self::MESSAGE => $msg, + self::CHILDREN => array() + ); + $this->_current[] = $error; + + // NEW CODE BELOW ... + // Top-level errors are either: + // TOKEN type, if $value is set appropriately, or + // "syntax" type, if $value is null + $new_struct = new HTMLPurifier_ErrorStruct(); + $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; + if ($token) { + $new_struct->value = clone $token; + } + if (is_int($line) && is_int($col)) { + if (isset($this->lines[$line][$col])) { + $struct = $this->lines[$line][$col]; + } else { + $struct = $this->lines[$line][$col] = $new_struct; + } + // These ksorts may present a performance problem + ksort($this->lines[$line], SORT_NUMERIC); + } else { + if (isset($this->lines[-1])) { + $struct = $this->lines[-1]; + } else { + $struct = $this->lines[-1] = $new_struct; + } + } + ksort($this->lines, SORT_NUMERIC); + + // Now, check if we need to operate on a lower structure + if (!empty($attr)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); + if (!$struct->value) { + $struct->value = array($attr, 'PUT VALUE HERE'); + } + } + if (!empty($cssprop)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); + if (!$struct->value) { + // if we tokenize CSS this might be a little more difficult to do + $struct->value = array($cssprop, 'PUT VALUE HERE'); + } + } + + // Ok, structs are all setup, now time to register the error + $struct->addError($severity, $msg); + } + + /** + * Retrieves raw error data for custom formatter to use + */ + public function getRaw() + { + return $this->errors; + } + + /** + * Default HTML formatting implementation for error messages + * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature + * @param array $errors Errors array to display; used for recursion. + * @return string + */ + public function getHTMLFormatted($config, $errors = null) + { + $ret = array(); + + $this->generator = new HTMLPurifier_Generator($config, $this->context); + if ($errors === null) { + $errors = $this->errors; + } + + // 'At line' message needs to be removed + + // generation code for new structure goes here. It needs to be recursive. + foreach ($this->lines as $line => $col_array) { + if ($line == -1) { + continue; + } + foreach ($col_array as $col => $struct) { + $this->_renderStruct($ret, $struct, $line, $col); + } + } + if (isset($this->lines[-1])) { + $this->_renderStruct($ret, $this->lines[-1]); + } + + if (empty($errors)) { + return '

' . $this->locale->getMessage('ErrorCollector: No errors') . '

'; + } else { + return '
  • ' . implode('
  • ', $ret) . '
'; + } + + } + + private function _renderStruct(&$ret, $struct, $line = null, $col = null) + { + $stack = array($struct); + $context_stack = array(array()); + while ($current = array_pop($stack)) { + $context = array_pop($context_stack); + foreach ($current->errors as $error) { + list($severity, $msg) = $error; + $string = ''; + $string .= '
'; + // W3C uses an icon to indicate the severity of the error. + $error = $this->locale->getErrorName($severity); + $string .= "$error "; + if (!is_null($line) && !is_null($col)) { + $string .= "Line $line, Column $col: "; + } else { + $string .= 'End of Document: '; + } + $string .= '' . $this->generator->escape($msg) . ' '; + $string .= '
'; + // Here, have a marker for the character on the column appropriate. + // Be sure to clip extremely long lines. + //$string .= '
';
+                //$string .= '';
+                //$string .= '
'; + $ret[] = $string; + } + foreach ($current->children as $array) { + $context[] = $current; + $stack = array_merge($stack, array_reverse($array, true)); + for ($i = count($array); $i > 0; $i--) { + $context_stack[] = $context; + } + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php new file mode 100644 index 00000000000..cf869d32124 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php @@ -0,0 +1,74 @@ +children[$type][$id])) { + $this->children[$type][$id] = new HTMLPurifier_ErrorStruct(); + $this->children[$type][$id]->type = $type; + } + return $this->children[$type][$id]; + } + + /** + * @param int $severity + * @param string $message + */ + public function addError($severity, $message) + { + $this->errors[] = array($severity, $message); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php new file mode 100644 index 00000000000..be85b4c560e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php @@ -0,0 +1,12 @@ +preFilter, + * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter, + * 1->postFilter. + * + * @note Methods are not declared abstract as it is perfectly legitimate + * for an implementation not to want anything to happen on a step + */ + +class HTMLPurifier_Filter +{ + + /** + * Name of the filter for identification purposes. + * @type string + */ + public $name; + + /** + * Pre-processor function, handles HTML before HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function preFilter($html, $config, $context) + { + return $html; + } + + /** + * Post-processor function, handles HTML after HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function postFilter($html, $config, $context) + { + return $html; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php new file mode 100644 index 00000000000..66f70b0fc00 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php @@ -0,0 +1,341 @@ + blocks from input HTML, cleans them up + * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks') + * so they can be used elsewhere in the document. + * + * @note + * See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for + * sample usage. + * + * @note + * This filter can also be used on stylesheets not included in the + * document--something purists would probably prefer. Just directly + * call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS() + */ +class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter +{ + /** + * @type string + */ + public $name = 'ExtractStyleBlocks'; + + /** + * @type array + */ + private $_styleMatches = array(); + + /** + * @type csstidy + */ + private $_tidy; + + /** + * @type HTMLPurifier_AttrDef_HTML_ID + */ + private $_id_attrdef; + + /** + * @type HTMLPurifier_AttrDef_CSS_Ident + */ + private $_class_attrdef; + + /** + * @type HTMLPurifier_AttrDef_Enum + */ + private $_enum_attrdef; + + public function __construct() + { + $this->_tidy = new csstidy(); + $this->_tidy->set_cfg('lowercase_s', false); + $this->_id_attrdef = new HTMLPurifier_AttrDef_HTML_ID(true); + $this->_class_attrdef = new HTMLPurifier_AttrDef_CSS_Ident(); + $this->_enum_attrdef = new HTMLPurifier_AttrDef_Enum( + array( + 'first-child', + 'link', + 'visited', + 'active', + 'hover', + 'focus' + ) + ); + } + + /** + * Save the contents of CSS blocks to style matches + * @param array $matches preg_replace style $matches array + */ + protected function styleCallback($matches) + { + $this->_styleMatches[] = $matches[1]; + } + + /** + * Removes inline + // we must not grab foo in a font-family prop). + if ($config->get('Filter.ExtractStyleBlocks.Escaping')) { + $css = str_replace( + array('<', '>', '&'), + array('\3C ', '\3E ', '\26 '), + $css + ); + } + return $css; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php new file mode 100644 index 00000000000..276d8362fa0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php @@ -0,0 +1,65 @@ +]+>.+?' . + '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; + $pre_replace = '\1'; + return preg_replace($pre_regex, $pre_replace, $html); + } + + /** + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function postFilter($html, $config, $context) + { + $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; + return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); + } + + /** + * @param $url + * @return string + */ + protected function armorUrl($url) + { + return str_replace('--', '--', $url); + } + + /** + * @param array $matches + * @return string + */ + protected function postFilterCallback($matches) + { + $url = $this->armorUrl($matches[1]); + return '' . + '' . + '' . + ''; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php new file mode 100644 index 00000000000..eb56e2dfa2e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php @@ -0,0 +1,286 @@ + tags. + * @type bool + */ + private $_scriptFix = false; + + /** + * Cache of HTMLDefinition during HTML output to determine whether or + * not attributes should be minimized. + * @type HTMLPurifier_HTMLDefinition + */ + private $_def; + + /** + * Cache of %Output.SortAttr. + * @type bool + */ + private $_sortAttr; + + /** + * Cache of %Output.FlashCompat. + * @type bool + */ + private $_flashCompat; + + /** + * Cache of %Output.FixInnerHTML. + * @type bool + */ + private $_innerHTMLFix; + + /** + * Stack for keeping track of object information when outputting IE + * compatibility code. + * @type array + */ + private $_flashStack = array(); + + /** + * Configuration for the generator + * @type HTMLPurifier_Config + */ + protected $config; + + /** + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + */ + public function __construct($config, $context) + { + $this->config = $config; + $this->_scriptFix = $config->get('Output.CommentScriptContents'); + $this->_innerHTMLFix = $config->get('Output.FixInnerHTML'); + $this->_sortAttr = $config->get('Output.SortAttr'); + $this->_flashCompat = $config->get('Output.FlashCompat'); + $this->_def = $config->getHTMLDefinition(); + $this->_xhtml = $this->_def->doctype->xml; + } + + /** + * Generates HTML from an array of tokens. + * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token + * @return string Generated HTML + */ + public function generateFromTokens($tokens) + { + if (!$tokens) { + return ''; + } + + // Basic algorithm + $html = ''; + for ($i = 0, $size = count($tokens); $i < $size; $i++) { + if ($this->_scriptFix && $tokens[$i]->name === 'script' + && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { + // script special case + // the contents of the script block must be ONE token + // for this to work. + $html .= $this->generateFromToken($tokens[$i++]); + $html .= $this->generateScriptFromToken($tokens[$i++]); + } + $html .= $this->generateFromToken($tokens[$i]); + } + + // Tidy cleanup + if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { + $tidy = new Tidy; + $tidy->parseString( + $html, + array( + 'indent'=> true, + 'output-xhtml' => $this->_xhtml, + 'show-body-only' => true, + 'indent-spaces' => 2, + 'wrap' => 68, + ), + 'utf8' + ); + $tidy->cleanRepair(); + $html = (string) $tidy; // explicit cast necessary + } + + // Normalize newlines to system defined value + if ($this->config->get('Core.NormalizeNewlines')) { + $nl = $this->config->get('Output.Newline'); + if ($nl === null) { + $nl = PHP_EOL; + } + if ($nl !== "\n") { + $html = str_replace("\n", $nl, $html); + } + } + return $html; + } + + /** + * Generates HTML from a single token. + * @param HTMLPurifier_Token $token HTMLPurifier_Token object. + * @return string Generated HTML + */ + public function generateFromToken($token) + { + if (!$token instanceof HTMLPurifier_Token) { + trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); + return ''; + + } elseif ($token instanceof HTMLPurifier_Token_Start) { + $attr = $this->generateAttributes($token->attr, $token->name); + if ($this->_flashCompat) { + if ($token->name == "object") { + $flash = new stdClass(); + $flash->attr = $token->attr; + $flash->param = array(); + $this->_flashStack[] = $flash; + } + } + return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_End) { + $_extra = ''; + if ($this->_flashCompat) { + if ($token->name == "object" && !empty($this->_flashStack)) { + // doesn't do anything for now + } + } + return $_extra . 'name . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { + $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; + } + $attr = $this->generateAttributes($token->attr, $token->name); + return '<' . $token->name . ($attr ? ' ' : '') . $attr . + ( $this->_xhtml ? ' /': '' ) //
v.
+ . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Text) { + return $this->escape($token->data, ENT_NOQUOTES); + + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + return ''; + } else { + return ''; + + } + } + + /** + * Special case processor for the contents of script tags + * @param HTMLPurifier_Token $token HTMLPurifier_Token object. + * @return string + * @warning This runs into problems if there's already a literal + * --> somewhere inside the script contents. + */ + public function generateScriptFromToken($token) + { + if (!$token instanceof HTMLPurifier_Token_Text) { + return $this->generateFromToken($token); + } + // Thanks + $data = preg_replace('#//\s*$#', '', $token->data); + return ''; + } + + /** + * Generates attribute declarations from attribute array. + * @note This does not include the leading or trailing space. + * @param array $assoc_array_of_attributes Attribute array + * @param string $element Name of element attributes are for, used to check + * attribute minimization. + * @return string Generated HTML fragment for insertion. + */ + public function generateAttributes($assoc_array_of_attributes, $element = '') + { + $html = ''; + if ($this->_sortAttr) { + ksort($assoc_array_of_attributes); + } + foreach ($assoc_array_of_attributes as $key => $value) { + if (!$this->_xhtml) { + // Remove namespaced attributes + if (strpos($key, ':') !== false) { + continue; + } + // Check if we should minimize the attribute: val="val" -> val + if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { + $html .= $key . ' '; + continue; + } + } + // Workaround for Internet Explorer innerHTML bug. + // Essentially, Internet Explorer, when calculating + // innerHTML, omits quotes if there are no instances of + // angled brackets, quotes or spaces. However, when parsing + // HTML (for example, when you assign to innerHTML), it + // treats backticks as quotes. Thus, + // `` + // becomes + // `` + // becomes + // + // Fortunately, all we need to do is trigger an appropriate + // quoting style, which we do by adding an extra space. + // This also is consistent with the W3C spec, which states + // that user agents may ignore leading or trailing + // whitespace (in fact, most don't, at least for attributes + // like alt, but an extra space at the end is barely + // noticeable). Still, we have a configuration knob for + // this, since this transformation is not necesary if you + // don't process user input with innerHTML or you don't plan + // on supporting Internet Explorer. + if ($this->_innerHTMLFix) { + if (strpos($value, '`') !== false) { + // check if correct quoting style would not already be + // triggered + if (strcspn($value, '"\' <>') === strlen($value)) { + // protect! + $value .= ' '; + } + } + } + $html .= $key.'="'.$this->escape($value).'" '; + } + return rtrim($html); + } + + /** + * Escapes raw text data. + * @todo This really ought to be protected, but until we have a facility + * for properly generating HTML here w/o using tokens, it stays + * public. + * @param string $string String data to escape for HTML. + * @param int $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is + * permissible for non-attribute output. + * @return string escaped data. + */ + public function escape($string, $quote = null) + { + // Workaround for APC bug on Mac Leopard reported by sidepodcast + // http://htmlpurifier.org/phorum/read.php?3,4823,4846 + if ($quote === null) { + $quote = ENT_COMPAT; + } + return htmlspecialchars($string, $quote, 'UTF-8'); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php new file mode 100644 index 00000000000..9b7b334dd95 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php @@ -0,0 +1,493 @@ +getAnonymousModule(); + if (!isset($module->info[$element_name])) { + $element = $module->addBlankElement($element_name); + } else { + $element = $module->info[$element_name]; + } + $element->attr[$attr_name] = $def; + } + + /** + * Adds a custom element to your HTML definition + * @see HTMLPurifier_HTMLModule::addElement() for detailed + * parameter and return value descriptions. + */ + public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) + { + $module = $this->getAnonymousModule(); + // assume that if the user is calling this, the element + // is safe. This may not be a good idea + $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); + return $element; + } + + /** + * Adds a blank element to your HTML definition, for overriding + * existing behavior + * @param string $element_name + * @return HTMLPurifier_ElementDef + * @see HTMLPurifier_HTMLModule::addBlankElement() for detailed + * parameter and return value descriptions. + */ + public function addBlankElement($element_name) + { + $module = $this->getAnonymousModule(); + $element = $module->addBlankElement($element_name); + return $element; + } + + /** + * Retrieves a reference to the anonymous module, so you can + * bust out advanced features without having to make your own + * module. + * @return HTMLPurifier_HTMLModule + */ + public function getAnonymousModule() + { + if (!$this->_anonModule) { + $this->_anonModule = new HTMLPurifier_HTMLModule(); + $this->_anonModule->name = 'Anonymous'; + } + return $this->_anonModule; + } + + private $_anonModule = null; + + // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- + + /** + * @type string + */ + public $type = 'HTML'; + + /** + * @type HTMLPurifier_HTMLModuleManager + */ + public $manager; + + /** + * Performs low-cost, preliminary initialization. + */ + public function __construct() + { + $this->manager = new HTMLPurifier_HTMLModuleManager(); + } + + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetup($config) + { + $this->processModules($config); + $this->setupConfigStuff($config); + unset($this->manager); + + // cleanup some of the element definitions + foreach ($this->info as $k => $v) { + unset($this->info[$k]->content_model); + unset($this->info[$k]->content_model_type); + } + } + + /** + * Extract out the information from the manager + * @param HTMLPurifier_Config $config + */ + protected function processModules($config) + { + if ($this->_anonModule) { + // for user specific changes + // this is late-loaded so we don't have to deal with PHP4 + // reference wonky-ness + $this->manager->addModule($this->_anonModule); + unset($this->_anonModule); + } + + $this->manager->setup($config); + $this->doctype = $this->manager->doctype; + + foreach ($this->manager->modules as $module) { + foreach ($module->info_tag_transform as $k => $v) { + if ($v === false) { + unset($this->info_tag_transform[$k]); + } else { + $this->info_tag_transform[$k] = $v; + } + } + foreach ($module->info_attr_transform_pre as $k => $v) { + if ($v === false) { + unset($this->info_attr_transform_pre[$k]); + } else { + $this->info_attr_transform_pre[$k] = $v; + } + } + foreach ($module->info_attr_transform_post as $k => $v) { + if ($v === false) { + unset($this->info_attr_transform_post[$k]); + } else { + $this->info_attr_transform_post[$k] = $v; + } + } + foreach ($module->info_injector as $k => $v) { + if ($v === false) { + unset($this->info_injector[$k]); + } else { + $this->info_injector[$k] = $v; + } + } + } + $this->info = $this->manager->getElements(); + $this->info_content_sets = $this->manager->contentSets->lookup; + } + + /** + * Sets up stuff based on config. We need a better way of doing this. + * @param HTMLPurifier_Config $config + */ + protected function setupConfigStuff($config) + { + $block_wrapper = $config->get('HTML.BlockWrapper'); + if (isset($this->info_content_sets['Block'][$block_wrapper])) { + $this->info_block_wrapper = $block_wrapper; + } else { + trigger_error( + 'Cannot use non-block element as block wrapper', + E_USER_ERROR + ); + } + + $parent = $config->get('HTML.Parent'); + $def = $this->manager->getElement($parent, true); + if ($def) { + $this->info_parent = $parent; + $this->info_parent_def = $def; + } else { + trigger_error( + 'Cannot use unrecognized element as parent', + E_USER_ERROR + ); + $this->info_parent_def = $this->manager->getElement($this->info_parent, true); + } + + // support template text + $support = "(for information on implementing this, see the support forums) "; + + // setup allowed elements ----------------------------------------- + + $allowed_elements = $config->get('HTML.AllowedElements'); + $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early + + if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { + $allowed = $config->get('HTML.Allowed'); + if (is_string($allowed)) { + list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); + } + } + + if (is_array($allowed_elements)) { + foreach ($this->info as $name => $d) { + if (!isset($allowed_elements[$name])) { + unset($this->info[$name]); + } + unset($allowed_elements[$name]); + } + // emit errors + foreach ($allowed_elements as $element => $d) { + $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! + trigger_error("Element '$element' is not supported $support", E_USER_WARNING); + } + } + + // setup allowed attributes --------------------------------------- + + $allowed_attributes_mutable = $allowed_attributes; // by copy! + if (is_array($allowed_attributes)) { + // This actually doesn't do anything, since we went away from + // global attributes. It's possible that userland code uses + // it, but HTMLModuleManager doesn't! + foreach ($this->info_global_attr as $attr => $x) { + $keys = array($attr, "*@$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) { + unset($this->info_global_attr[$attr]); + } + } + + foreach ($this->info as $tag => $info) { + foreach ($info->attr as $attr => $x) { + $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) { + if ($this->info[$tag]->attr[$attr]->required) { + trigger_error( + "Required attribute '$attr' in element '$tag' " . + "was not allowed, which means '$tag' will not be allowed either", + E_USER_WARNING + ); + } + unset($this->info[$tag]->attr[$attr]); + } + } + } + // emit errors + foreach ($allowed_attributes_mutable as $elattr => $d) { + $bits = preg_split('/[.@]/', $elattr, 2); + $c = count($bits); + switch ($c) { + case 2: + if ($bits[0] !== '*') { + $element = htmlspecialchars($bits[0]); + $attribute = htmlspecialchars($bits[1]); + if (!isset($this->info[$element])) { + trigger_error( + "Cannot allow attribute '$attribute' if element " . + "'$element' is not allowed/supported $support" + ); + } else { + trigger_error( + "Attribute '$attribute' in element '$element' not supported $support", + E_USER_WARNING + ); + } + break; + } + // otherwise fall through + case 1: + $attribute = htmlspecialchars($bits[0]); + trigger_error( + "Global attribute '$attribute' is not ". + "supported in any elements $support", + E_USER_WARNING + ); + break; + } + } + } + + // setup forbidden elements --------------------------------------- + + $forbidden_elements = $config->get('HTML.ForbiddenElements'); + $forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); + + foreach ($this->info as $tag => $info) { + if (isset($forbidden_elements[$tag])) { + unset($this->info[$tag]); + continue; + } + foreach ($info->attr as $attr => $x) { + if (isset($forbidden_attributes["$tag@$attr"]) || + isset($forbidden_attributes["*@$attr"]) || + isset($forbidden_attributes[$attr]) + ) { + unset($this->info[$tag]->attr[$attr]); + continue; + } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually + // $tag.$attr are not user supplied, so no worries! + trigger_error( + "Error with $tag.$attr: tag.attr syntax not supported for " . + "HTML.ForbiddenAttributes; use tag@attr instead", + E_USER_WARNING + ); + } + } + } + foreach ($forbidden_attributes as $key => $v) { + if (strlen($key) < 2) { + continue; + } + if ($key[0] != '*') { + continue; + } + if ($key[1] == '.') { + trigger_error( + "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", + E_USER_WARNING + ); + } + } + + // setup injectors ----------------------------------------------------- + foreach ($this->info_injector as $i => $injector) { + if ($injector->checkNeeded($config) !== false) { + // remove injector that does not have it's required + // elements/attributes present, and is thus not needed. + unset($this->info_injector[$i]); + } + } + } + + /** + * Parses a TinyMCE-flavored Allowed Elements and Attributes list into + * separate lists for processing. Format is element[attr1|attr2],element2... + * @warning Although it's largely drawn from TinyMCE's implementation, + * it is different, and you'll probably have to modify your lists + * @param array $list String list to parse + * @return array + * @todo Give this its own class, probably static interface + */ + public function parseTinyMCEAllowedList($list) + { + $list = str_replace(array(' ', "\t"), '', $list); + + $elements = array(); + $attributes = array(); + + $chunks = preg_split('/(,|[\n\r]+)/', $list); + foreach ($chunks as $chunk) { + if (empty($chunk)) { + continue; + } + // remove TinyMCE element control characters + if (!strpos($chunk, '[')) { + $element = $chunk; + $attr = false; + } else { + list($element, $attr) = explode('[', $chunk); + } + if ($element !== '*') { + $elements[$element] = true; + } + if (!$attr) { + continue; + } + $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] + $attr = explode('|', $attr); + foreach ($attr as $key) { + $attributes["$element.$key"] = true; + } + } + return array($elements, $attributes); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php new file mode 100644 index 00000000000..6d898f80cd0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php @@ -0,0 +1,284 @@ +info, since the object's data is only info, + * with extra behavior associated with it. + * @type array + */ + public $attr_collections = array(); + + /** + * Associative array of deprecated tag name to HTMLPurifier_TagTransform. + * @type array + */ + public $info_tag_transform = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed before validation. + * @type array + */ + public $info_attr_transform_pre = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed after validation. + * @type array + */ + public $info_attr_transform_post = array(); + + /** + * List of HTMLPurifier_Injector to be performed during well-formedness fixing. + * An injector will only be invoked if all of it's pre-requisites are met; + * if an injector fails setup, there will be no error; it will simply be + * silently disabled. + * @type array + */ + public $info_injector = array(); + + /** + * Boolean flag that indicates whether or not getChildDef is implemented. + * For optimization reasons: may save a call to a function. Be sure + * to set it if you do implement getChildDef(), otherwise it will have + * no effect! + * @type bool + */ + public $defines_child_def = false; + + /** + * Boolean flag whether or not this module is safe. If it is not safe, all + * of its members are unsafe. Modules are safe by default (this might be + * slightly dangerous, but it doesn't make much sense to force HTML Purifier, + * which is based off of safe HTML, to explicitly say, "This is safe," even + * though there are modules which are "unsafe") + * + * @type bool + * @note Previously, safety could be applied at an element level granularity. + * We've removed this ability, so in order to add "unsafe" elements + * or attributes, a dedicated module with this property set to false + * must be used. + */ + public $safe = true; + + /** + * Retrieves a proper HTMLPurifier_ChildDef subclass based on + * content_model and content_model_type member variables of + * the HTMLPurifier_ElementDef class. There is a similar function + * in HTMLPurifier_HTMLDefinition. + * @param HTMLPurifier_ElementDef $def + * @return HTMLPurifier_ChildDef subclass + */ + public function getChildDef($def) + { + return false; + } + + // -- Convenience ----------------------------------------------------- + + /** + * Convenience function that sets up a new element + * @param string $element Name of element to add + * @param string|bool $type What content set should element be registered to? + * Set as false to skip this step. + * @param string|HTMLPurifier_ChildDef $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @param array|string $attr_includes What attribute collections to register to + * element? + * @param array $attr What unique attributes does the element define? + * @see HTMLPurifier_ElementDef:: for in-depth descriptions of these parameters. + * @return HTMLPurifier_ElementDef Created element definition object, so you + * can set advanced parameters + */ + public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) + { + $this->elements[] = $element; + // parse content_model + list($content_model_type, $content_model) = $this->parseContents($contents); + // merge in attribute inclusions + $this->mergeInAttrIncludes($attr, $attr_includes); + // add element to content sets + if ($type) { + $this->addElementToContentSet($element, $type); + } + // create element + $this->info[$element] = HTMLPurifier_ElementDef::create( + $content_model, + $content_model_type, + $attr + ); + // literal object $contents means direct child manipulation + if (!is_string($contents)) { + $this->info[$element]->child = $contents; + } + return $this->info[$element]; + } + + /** + * Convenience function that creates a totally blank, non-standalone + * element. + * @param string $element Name of element to create + * @return HTMLPurifier_ElementDef Created element + */ + public function addBlankElement($element) + { + if (!isset($this->info[$element])) { + $this->elements[] = $element; + $this->info[$element] = new HTMLPurifier_ElementDef(); + $this->info[$element]->standalone = false; + } else { + trigger_error("Definition for $element already exists in module, cannot redefine"); + } + return $this->info[$element]; + } + + /** + * Convenience function that registers an element to a content set + * @param string $element Element to register + * @param string $type Name content set (warning: case sensitive, usually upper-case + * first letter) + */ + public function addElementToContentSet($element, $type) + { + if (!isset($this->content_sets[$type])) { + $this->content_sets[$type] = ''; + } else { + $this->content_sets[$type] .= ' | '; + } + $this->content_sets[$type] .= $element; + } + + /** + * Convenience function that transforms single-string contents + * into separate content model and content model type + * @param string $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @return array + * @note If contents is an object, an array of two nulls will be + * returned, and the callee needs to take the original $contents + * and use it directly. + */ + public function parseContents($contents) + { + if (!is_string($contents)) { + return array(null, null); + } // defer + switch ($contents) { + // check for shorthand content model forms + case 'Empty': + return array('empty', ''); + case 'Inline': + return array('optional', 'Inline | #PCDATA'); + case 'Flow': + return array('optional', 'Flow | #PCDATA'); + } + list($content_model_type, $content_model) = explode(':', $contents); + $content_model_type = strtolower(trim($content_model_type)); + $content_model = trim($content_model); + return array($content_model_type, $content_model); + } + + /** + * Convenience function that merges a list of attribute includes into + * an attribute array. + * @param array $attr Reference to attr array to modify + * @param array $attr_includes Array of includes / string include to merge in + */ + public function mergeInAttrIncludes(&$attr, $attr_includes) + { + if (!is_array($attr_includes)) { + if (empty($attr_includes)) { + $attr_includes = array(); + } else { + $attr_includes = array($attr_includes); + } + } + $attr[0] = $attr_includes; + } + + /** + * Convenience function that generates a lookup table with boolean + * true as value. + * @param string $list List of values to turn into a lookup + * @note You can also pass an arbitrary number of arguments in + * place of the regular argument + * @return array array equivalent of list + */ + public function makeLookup($list) + { + if (is_string($list)) { + $list = func_get_args(); + } + $ret = array(); + foreach ($list as $value) { + if (is_null($value)) { + continue; + } + $ret[$value] = true; + } + return $ret; + } + + /** + * Lazy load construction of the module after determining whether + * or not it's needed, and also when a finalized configuration object + * is available. + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php new file mode 100644 index 00000000000..1e67c790d01 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php @@ -0,0 +1,44 @@ + array('dir' => false) + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $bdo = $this->addElement( + 'bdo', + 'Inline', + 'Inline', + array('Core', 'Lang'), + array( + 'dir' => 'Enum#ltr,rtl', // required + // The Abstract Module specification has the attribute + // inclusions wrong for bdo: bdo allows Lang + ) + ); + $bdo->attr_transform_post[] = new HTMLPurifier_AttrTransform_BdoDir(); + + $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl'; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php new file mode 100644 index 00000000000..a96ab1bef11 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php @@ -0,0 +1,31 @@ + array( + 0 => array('Style'), + // 'xml:space' => false, + 'class' => 'Class', + 'id' => 'ID', + 'title' => 'CDATA', + ), + 'Lang' => array(), + 'I18N' => array( + 0 => array('Lang'), // proprietary, for xml:lang/lang + ), + 'Common' => array( + 0 => array('Core', 'I18N') + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php new file mode 100644 index 00000000000..a9042a3577b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php @@ -0,0 +1,55 @@ + 'URI', + // 'datetime' => 'Datetime', // not implemented + ); + $this->addElement('del', 'Inline', $contents, 'Common', $attr); + $this->addElement('ins', 'Inline', $contents, 'Common', $attr); + } + + // HTML 4.01 specifies that ins/del must not contain block + // elements when used in an inline context, chameleon is + // a complicated workaround to acheive this effect + + // Inline context ! Block context (exclamation mark is + // separator, see getChildDef for parsing) + + /** + * @type bool + */ + public $defines_child_def = true; + + /** + * @param HTMLPurifier_ElementDef $def + * @return HTMLPurifier_ChildDef_Chameleon + */ + public function getChildDef($def) + { + if ($def->content_model_type != 'chameleon') { + return false; + } + $value = explode('!', $def->content_model); + return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php new file mode 100644 index 00000000000..eb0edcffd52 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php @@ -0,0 +1,194 @@ + 'Form', + 'Inline' => 'Formctrl', + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + if ($config->get('HTML.Forms')) { + $this->safe = true; + } + + $form = $this->addElement( + 'form', + 'Form', + 'Required: Heading | List | Block | fieldset', + 'Common', + array( + 'accept' => 'ContentTypes', + 'accept-charset' => 'Charsets', + 'action*' => 'URI', + 'method' => 'Enum#get,post', + // really ContentType, but these two are the only ones used today + 'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data', + ) + ); + $form->excludes = array('form' => true); + + $input = $this->addElement( + 'input', + 'Formctrl', + 'Empty', + 'Common', + array( + 'accept' => 'ContentTypes', + 'accesskey' => 'Character', + 'alt' => 'Text', + 'checked' => 'Bool#checked', + 'disabled' => 'Bool#disabled', + 'maxlength' => 'Number', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'size' => 'Number', + 'src' => 'URI#embedded', + 'tabindex' => 'Number', + 'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image', + 'value' => 'CDATA', + ) + ); + $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input(); + + $this->addElement( + 'select', + 'Formctrl', + 'Required: optgroup | option', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'multiple' => 'Bool#multiple', + 'name' => 'CDATA', + 'size' => 'Number', + 'tabindex' => 'Number', + ) + ); + + $this->addElement( + 'option', + false, + 'Optional: #PCDATA', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'label' => 'Text', + 'selected' => 'Bool#selected', + 'value' => 'CDATA', + ) + ); + // It's illegal for there to be more than one selected, but not + // be multiple. Also, no selected means undefined behavior. This might + // be difficult to implement; perhaps an injector, or a context variable. + + $textarea = $this->addElement( + 'textarea', + 'Formctrl', + 'Optional: #PCDATA', + 'Common', + array( + 'accesskey' => 'Character', + 'cols*' => 'Number', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'rows*' => 'Number', + 'tabindex' => 'Number', + ) + ); + $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea(); + + $button = $this->addElement( + 'button', + 'Formctrl', + 'Optional: #PCDATA | Heading | List | Block | Inline', + 'Common', + array( + 'accesskey' => 'Character', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'tabindex' => 'Number', + 'type' => 'Enum#button,submit,reset', + 'value' => 'CDATA', + ) + ); + + // For exclusions, ideally we'd specify content sets, not literal elements + $button->excludes = $this->makeLookup( + 'form', + 'fieldset', // Form + 'input', + 'select', + 'textarea', + 'label', + 'button', // Formctrl + 'a', // as per HTML 4.01 spec, this is omitted by modularization + 'isindex', + 'iframe' // legacy items + ); + + // Extra exclusion: img usemap="" is not permitted within this element. + // We'll omit this for now, since we don't have any good way of + // indicating it yet. + + // This is HIGHLY user-unfriendly; we need a custom child-def for this + $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common'); + + $label = $this->addElement( + 'label', + 'Formctrl', + 'Optional: #PCDATA | Inline', + 'Common', + array( + 'accesskey' => 'Character', + // 'for' => 'IDREF', // IDREF not implemented, cannot allow + ) + ); + $label->excludes = array('label' => true); + + $this->addElement( + 'legend', + false, + 'Optional: #PCDATA | Inline', + 'Common', + array( + 'accesskey' => 'Character', + ) + ); + + $this->addElement( + 'optgroup', + false, + 'Required: option', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'label*' => 'Text', + ) + ); + // Don't forget an injector for . This one's a little complex + // because it maps to multiple elements. + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php new file mode 100644 index 00000000000..72d7a31e687 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php @@ -0,0 +1,40 @@ +addElement( + 'a', + 'Inline', + 'Inline', + 'Common', + array( + // 'accesskey' => 'Character', + // 'charset' => 'Charset', + 'href' => 'URI', + // 'hreflang' => 'LanguageCode', + 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), + 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), + // 'tabindex' => 'Number', + // 'type' => 'ContentType', + ) + ); + $a->formatting = true; + $a->excludes = array('a' => true); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php new file mode 100644 index 00000000000..f7e7c91c02c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php @@ -0,0 +1,51 @@ +get('HTML.SafeIframe')) { + $this->safe = true; + } + $this->addElement( + 'iframe', + 'Inline', + 'Flow', + 'Common', + array( + 'src' => 'URI#embedded', + 'width' => 'Length', + 'height' => 'Length', + 'name' => 'ID', + 'scrolling' => 'Enum#yes,no,auto', + 'frameborder' => 'Enum#0,1', + 'longdesc' => 'URI', + 'marginheight' => 'Pixels', + 'marginwidth' => 'Pixels', + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php new file mode 100644 index 00000000000..0f5fdb3baf2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php @@ -0,0 +1,49 @@ +get('HTML.MaxImgLength'); + $img = $this->addElement( + 'img', + 'Inline', + 'Empty', + 'Common', + array( + 'alt*' => 'Text', + // According to the spec, it's Length, but percents can + // be abused, so we allow only Pixels. + 'height' => 'Pixels#' . $max, + 'width' => 'Pixels#' . $max, + 'longdesc' => 'URI', + 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded + ) + ); + if ($max === null || $config->get('HTML.Trusted')) { + $img->attr['height'] = + $img->attr['width'] = 'Length'; + } + + // kind of strange, but splitting things up would be inefficient + $img->attr_transform_pre[] = + $img->attr_transform_post[] = + new HTMLPurifier_AttrTransform_ImgRequired(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php new file mode 100644 index 00000000000..86b52995799 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php @@ -0,0 +1,186 @@ +addElement( + 'basefont', + 'Inline', + 'Empty', + null, + array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + 'id' => 'ID' + ) + ); + $this->addElement('center', 'Block', 'Flow', 'Common'); + $this->addElement( + 'dir', + 'Block', + 'Required: li', + 'Common', + array( + 'compact' => 'Bool#compact' + ) + ); + $this->addElement( + 'font', + 'Inline', + 'Inline', + array('Core', 'I18N'), + array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + ) + ); + $this->addElement( + 'menu', + 'Block', + 'Required: li', + 'Common', + array( + 'compact' => 'Bool#compact' + ) + ); + + $s = $this->addElement('s', 'Inline', 'Inline', 'Common'); + $s->formatting = true; + + $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common'); + $strike->formatting = true; + + $u = $this->addElement('u', 'Inline', 'Inline', 'Common'); + $u->formatting = true; + + // setup modifications to old elements + + $align = 'Enum#left,right,center,justify'; + + $address = $this->addBlankElement('address'); + $address->content_model = 'Inline | #PCDATA | p'; + $address->content_model_type = 'optional'; + $address->child = false; + + $blockquote = $this->addBlankElement('blockquote'); + $blockquote->content_model = 'Flow | #PCDATA'; + $blockquote->content_model_type = 'optional'; + $blockquote->child = false; + + $br = $this->addBlankElement('br'); + $br->attr['clear'] = 'Enum#left,all,right,none'; + + $caption = $this->addBlankElement('caption'); + $caption->attr['align'] = 'Enum#top,bottom,left,right'; + + $div = $this->addBlankElement('div'); + $div->attr['align'] = $align; + + $dl = $this->addBlankElement('dl'); + $dl->attr['compact'] = 'Bool#compact'; + + for ($i = 1; $i <= 6; $i++) { + $h = $this->addBlankElement("h$i"); + $h->attr['align'] = $align; + } + + $hr = $this->addBlankElement('hr'); + $hr->attr['align'] = $align; + $hr->attr['noshade'] = 'Bool#noshade'; + $hr->attr['size'] = 'Pixels'; + $hr->attr['width'] = 'Length'; + + $img = $this->addBlankElement('img'); + $img->attr['align'] = 'IAlign'; + $img->attr['border'] = 'Pixels'; + $img->attr['hspace'] = 'Pixels'; + $img->attr['vspace'] = 'Pixels'; + + // figure out this integer business + + $li = $this->addBlankElement('li'); + $li->attr['value'] = new HTMLPurifier_AttrDef_Integer(); + $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle'; + + $ol = $this->addBlankElement('ol'); + $ol->attr['compact'] = 'Bool#compact'; + $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer(); + $ol->attr['type'] = 'Enum#s:1,i,I,a,A'; + + $p = $this->addBlankElement('p'); + $p->attr['align'] = $align; + + $pre = $this->addBlankElement('pre'); + $pre->attr['width'] = 'Number'; + + // script omitted + + $table = $this->addBlankElement('table'); + $table->attr['align'] = 'Enum#left,center,right'; + $table->attr['bgcolor'] = 'Color'; + + $tr = $this->addBlankElement('tr'); + $tr->attr['bgcolor'] = 'Color'; + + $th = $this->addBlankElement('th'); + $th->attr['bgcolor'] = 'Color'; + $th->attr['height'] = 'Length'; + $th->attr['nowrap'] = 'Bool#nowrap'; + $th->attr['width'] = 'Length'; + + $td = $this->addBlankElement('td'); + $td->attr['bgcolor'] = 'Color'; + $td->attr['height'] = 'Length'; + $td->attr['nowrap'] = 'Bool#nowrap'; + $td->attr['width'] = 'Length'; + + $ul = $this->addBlankElement('ul'); + $ul->attr['compact'] = 'Bool#compact'; + $ul->attr['type'] = 'Enum#square,disc,circle'; + + // "safe" modifications to "unsafe" elements + // WARNING: If you want to add support for an unsafe, legacy + // attribute, make a new TrustedLegacy module with the trusted + // bit set appropriately + + $form = $this->addBlankElement('form'); + $form->content_model = 'Flow | #PCDATA'; + $form->content_model_type = 'optional'; + $form->attr['target'] = 'FrameTarget'; + + $input = $this->addBlankElement('input'); + $input->attr['align'] = 'IAlign'; + + $legend = $this->addBlankElement('legend'); + $legend->attr['align'] = 'LAlign'; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php new file mode 100644 index 00000000000..7a20ff701c6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php @@ -0,0 +1,51 @@ + 'List'); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $ol = $this->addElement('ol', 'List', new HTMLPurifier_ChildDef_List(), 'Common'); + $ul = $this->addElement('ul', 'List', new HTMLPurifier_ChildDef_List(), 'Common'); + // XXX The wrap attribute is handled by MakeWellFormed. This is all + // quite unsatisfactory, because we generated this + // *specifically* for lists, and now a big chunk of the handling + // is done properly by the List ChildDef. So actually, we just + // want enough information to make autoclosing work properly, + // and then hand off the tricky stuff to the ChildDef. + $ol->wrap = 'li'; + $ul->wrap = 'li'; + $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); + + $this->addElement('li', false, 'Flow', 'Common'); + + $this->addElement('dd', false, 'Flow', 'Common'); + $this->addElement('dt', false, 'Inline', 'Common'); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php new file mode 100644 index 00000000000..60c0545154c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php @@ -0,0 +1,26 @@ +addBlankElement($name); + $element->attr['name'] = 'CDATA'; + if (!$config->get('HTML.Attr.Name.UseCDATA')) { + $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync(); + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php new file mode 100644 index 00000000000..dc9410a8957 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php @@ -0,0 +1,25 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Nofollow(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php new file mode 100644 index 00000000000..da722253ac8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php @@ -0,0 +1,20 @@ + array( + 'lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php new file mode 100644 index 00000000000..2f9efc5c88f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php @@ -0,0 +1,62 @@ + to cater to legacy browsers: this + * module does not allow this sort of behavior + */ +class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule +{ + /** + * @type string + */ + public $name = 'Object'; + + /** + * @type bool + */ + public $safe = false; + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $this->addElement( + 'object', + 'Inline', + 'Optional: #PCDATA | Flow | param', + 'Common', + array( + 'archive' => 'URI', + 'classid' => 'URI', + 'codebase' => 'URI', + 'codetype' => 'Text', + 'data' => 'URI', + 'declare' => 'Bool#declare', + 'height' => 'Length', + 'name' => 'CDATA', + 'standby' => 'Text', + 'tabindex' => 'Number', + 'type' => 'ContentType', + 'width' => 'Length' + ) + ); + + $this->addElement( + 'param', + false, + 'Empty', + null, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'type' => 'Text', + 'value' => 'Text', + 'valuetype' => 'Enum#data,ref,object' + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php new file mode 100644 index 00000000000..6458ce9d883 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php @@ -0,0 +1,42 @@ +addElement('hr', 'Block', 'Empty', 'Common'); + $this->addElement('sub', 'Inline', 'Inline', 'Common'); + $this->addElement('sup', 'Inline', 'Inline', 'Common'); + $b = $this->addElement('b', 'Inline', 'Inline', 'Common'); + $b->formatting = true; + $big = $this->addElement('big', 'Inline', 'Inline', 'Common'); + $big->formatting = true; + $i = $this->addElement('i', 'Inline', 'Inline', 'Common'); + $i->formatting = true; + $small = $this->addElement('small', 'Inline', 'Inline', 'Common'); + $small->formatting = true; + $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common'); + $tt->formatting = true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php new file mode 100644 index 00000000000..5ee3c8e67f0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php @@ -0,0 +1,40 @@ +addElement( + 'marquee', + 'Inline', + 'Flow', + 'Common', + array( + 'direction' => 'Enum#left,right,up,down', + 'behavior' => 'Enum#alternate', + 'width' => 'Length', + 'height' => 'Length', + 'scrolldelay' => 'Number', + 'scrollamount' => 'Number', + 'loop' => 'Number', + 'bgcolor' => 'Color', + 'hspace' => 'Pixels', + 'vspace' => 'Pixels', + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php new file mode 100644 index 00000000000..a0d48924da5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php @@ -0,0 +1,36 @@ +addElement( + 'ruby', + 'Inline', + 'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))', + 'Common' + ); + $this->addElement('rbc', false, 'Required: rb', 'Common'); + $this->addElement('rtc', false, 'Required: rt', 'Common'); + $rb = $this->addElement('rb', false, 'Inline', 'Common'); + $rb->excludes = array('ruby' => true); + $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number')); + $rt->excludes = array('ruby' => true); + $this->addElement('rp', false, 'Optional: #PCDATA', 'Common'); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php new file mode 100644 index 00000000000..04e6689ea6a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -0,0 +1,40 @@ +get('HTML.MaxImgLength'); + $embed = $this->addElement( + 'embed', + 'Inline', + 'Empty', + 'Common', + array( + 'src*' => 'URI#embedded', + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'allowscriptaccess' => 'Enum#never', + 'allownetworking' => 'Enum#internal', + 'flashvars' => 'Text', + 'wmode' => 'Enum#window,transparent,opaque', + 'name' => 'ID', + ) + ); + $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php new file mode 100644 index 00000000000..1297f80a39c --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php @@ -0,0 +1,62 @@ +get('HTML.MaxImgLength'); + $object = $this->addElement( + 'object', + 'Inline', + 'Optional: param | Flow | #PCDATA', + 'Common', + array( + // While technically not required by the spec, we're forcing + // it to this value. + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'data' => 'URI#embedded', + 'codebase' => new HTMLPurifier_AttrDef_Enum( + array( + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0' + ) + ), + ) + ); + $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); + + $param = $this->addElement( + 'param', + false, + 'Empty', + false, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'value' => 'Text' + ) + ); + $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam(); + $this->info_injector[] = 'SafeObject'; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php new file mode 100644 index 00000000000..aea7584c374 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php @@ -0,0 +1,40 @@ +get('HTML.SafeScripting'); + $script = $this->addElement( + 'script', + 'Inline', + 'Optional:', // Not `Empty` to not allow to autoclose the #i', '', $html); + } + + return $html; + } + + /** + * Takes a string of HTML (fragment or document) and returns the content + * @todo Consider making protected + */ + public function extractBody($html) + { + $matches = array(); + $result = preg_match('|(.*?)]*>(.*)|is', $html, $matches); + if ($result) { + // Make sure it's not in a comment + $comment_start = strrpos($matches[1], ''); + if ($comment_start === false || + ($comment_end !== false && $comment_end > $comment_start)) { + return $matches[2]; + } + } + return $html; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php new file mode 100644 index 00000000000..ca5f25b849f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php @@ -0,0 +1,338 @@ +factory = new HTMLPurifier_TokenFactory(); + } + + /** + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + */ + public function tokenizeHTML($html, $config, $context) + { + $html = $this->normalize($html, $config, $context); + + // attempt to armor stray angled brackets that cannot possibly + // form tags and thus are probably being used as emoticons + if ($config->get('Core.AggressivelyFixLt')) { + $char = '[^a-z!\/]'; + $comment = "/|\z)/is"; + $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html); + do { + $old = $html; + $html = preg_replace("/<($char)/i", '<\\1', $html); + } while ($html !== $old); + $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments + } + + // preprocess html, essential for UTF-8 + $html = $this->wrapHTML($html, $config, $context); + + $doc = new DOMDocument(); + $doc->encoding = 'UTF-8'; // theoretically, the above has this covered + + $options = 0; + if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) { + $options |= LIBXML_PARSEHUGE; + } + + set_error_handler(array($this, 'muteErrorHandler')); + // loadHTML() fails on PHP 5.3 when second parameter is given + if ($options) { + $doc->loadHTML($html, $options); + } else { + $doc->loadHTML($html); + } + restore_error_handler(); + + $body = $doc->getElementsByTagName('html')->item(0)-> // + getElementsByTagName('body')->item(0); // + + $div = $body->getElementsByTagName('div')->item(0); //
+ $tokens = array(); + $this->tokenizeDOM($div, $tokens, $config); + // If the div has a sibling, that means we tripped across + // a premature
tag. So remove the div we parsed, + // and then tokenize the rest of body. We can't tokenize + // the sibling directly as we'll lose the tags in that case. + if ($div->nextSibling) { + $body->removeChild($div); + $this->tokenizeDOM($body, $tokens, $config); + } + return $tokens; + } + + /** + * Iterative function that tokenizes a node, putting it into an accumulator. + * To iterate is human, to recurse divine - L. Peter Deutsch + * @param DOMNode $node DOMNode to be tokenized. + * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. + * @return HTMLPurifier_Token of node appended to previously passed tokens. + */ + protected function tokenizeDOM($node, &$tokens, $config) + { + $level = 0; + $nodes = array($level => new HTMLPurifier_Queue(array($node))); + $closingNodes = array(); + do { + while (!$nodes[$level]->isEmpty()) { + $node = $nodes[$level]->shift(); // FIFO + $collect = $level > 0 ? true : false; + $needEndingTag = $this->createStartNode($node, $tokens, $collect, $config); + if ($needEndingTag) { + $closingNodes[$level][] = $node; + } + if ($node->childNodes && $node->childNodes->length) { + $level++; + $nodes[$level] = new HTMLPurifier_Queue(); + foreach ($node->childNodes as $childNode) { + $nodes[$level]->push($childNode); + } + } + } + $level--; + if ($level && isset($closingNodes[$level])) { + while ($node = array_pop($closingNodes[$level])) { + $this->createEndNode($node, $tokens); + } + } + } while ($level > 0); + } + + /** + * Portably retrieve the tag name of a node; deals with older versions + * of libxml like 2.7.6 + * @param DOMNode $node + */ + protected function getTagName($node) + { + if (isset($node->tagName)) { + return $node->tagName; + } else if (isset($node->nodeName)) { + return $node->nodeName; + } else if (isset($node->localName)) { + return $node->localName; + } + return null; + } + + /** + * Portably retrieve the data of a node; deals with older versions + * of libxml like 2.7.6 + * @param DOMNode $node + */ + protected function getData($node) + { + if (isset($node->data)) { + return $node->data; + } else if (isset($node->nodeValue)) { + return $node->nodeValue; + } else if (isset($node->textContent)) { + return $node->textContent; + } + return null; + } + + + /** + * @param DOMNode $node DOMNode to be tokenized. + * @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens. + * @param bool $collect Says whether or start and close are collected, set to + * false at first recursion because it's the implicit DIV + * tag you're dealing with. + * @return bool if the token needs an endtoken + * @todo data and tagName properties don't seem to exist in DOMNode? + */ + protected function createStartNode($node, &$tokens, $collect, $config) + { + // intercept non element nodes. WE MUST catch all of them, + // but we're not getting the character reference nodes because + // those should have been preprocessed + if ($node->nodeType === XML_TEXT_NODE) { + $data = $this->getData($node); // Handle variable data property + if ($data !== null) { + $tokens[] = $this->factory->createText($data); + } + return false; + } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) { + // undo libxml's special treatment of )#si', + array($this, 'scriptCallback'), + $html + ); + } + + $html = $this->normalize($html, $config, $context); + + $cursor = 0; // our location in the text + $inside_tag = false; // whether or not we're parsing the inside of a tag + $array = array(); // result array + + // This is also treated to mean maintain *column* numbers too + $maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); + + if ($maintain_line_numbers === null) { + // automatically determine line numbering by checking + // if error collection is on + $maintain_line_numbers = $config->get('Core.CollectErrors'); + } + + if ($maintain_line_numbers) { + $current_line = 1; + $current_col = 0; + $length = strlen($html); + } else { + $current_line = false; + $current_col = false; + $length = false; + } + $context->register('CurrentLine', $current_line); + $context->register('CurrentCol', $current_col); + $nl = "\n"; + // how often to manually recalculate. This will ALWAYS be right, + // but it's pretty wasteful. Set to 0 to turn off + $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + // for testing synchronization + $loops = 0; + + while (++$loops) { + // $cursor is either at the start of a token, or inside of + // a tag (i.e. there was a < immediately before it), as indicated + // by $inside_tag + + if ($maintain_line_numbers) { + // $rcursor, however, is always at the start of a token. + $rcursor = $cursor - (int)$inside_tag; + + // Column number is cheap, so we calculate it every round. + // We're interested at the *end* of the newline string, so + // we need to add strlen($nl) == 1 to $nl_pos before subtracting it + // from our "rcursor" position. + $nl_pos = strrpos($html, $nl, $rcursor - $length); + $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); + + // recalculate lines + if ($synchronize_interval && // synchronization is on + $cursor > 0 && // cursor is further than zero + $loops % $synchronize_interval === 0) { // time to synchronize! + $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); + } + } + + $position_next_lt = strpos($html, '<', $cursor); + $position_next_gt = strpos($html, '>', $cursor); + + // triggers on "asdf" but not "asdf " + // special case to set up context + if ($position_next_lt === $cursor) { + $inside_tag = true; + $cursor++; + } + + if (!$inside_tag && $position_next_lt !== false) { + // We are not inside tag and there still is another tag to parse + $token = new + HTMLPurifier_Token_Text( + $this->parseText( + substr( + $html, + $cursor, + $position_next_lt - $cursor + ), $config + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); + } + $array[] = $token; + $cursor = $position_next_lt + 1; + $inside_tag = true; + continue; + } elseif (!$inside_tag) { + // We are not inside tag but there are no more tags + // If we're already at the end, break + if ($cursor === strlen($html)) { + break; + } + // Create Text of rest of string + $token = new + HTMLPurifier_Token_Text( + $this->parseText( + substr( + $html, + $cursor + ), $config + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } + $array[] = $token; + break; + } elseif ($inside_tag && $position_next_gt !== false) { + // We are in tag and it is well formed + // Grab the internals of the tag + $strlen_segment = $position_next_gt - $cursor; + + if ($strlen_segment < 1) { + // there's nothing to process! + $token = new HTMLPurifier_Token_Text('<'); + $cursor++; + continue; + } + + $segment = substr($html, $cursor, $strlen_segment); + + if ($segment === false) { + // somehow, we attempted to access beyond the end of + // the string, defense-in-depth, reported by Nate Abele + break; + } + + // Check if it's a comment + if (substr($segment, 0, 3) === '!--') { + // re-determine segment length, looking for --> + $position_comment_end = strpos($html, '-->', $cursor); + if ($position_comment_end === false) { + // uh oh, we have a comment that extends to + // infinity. Can't be helped: set comment + // end position to end of string + if ($e) { + $e->send(E_WARNING, 'Lexer: Unclosed comment'); + } + $position_comment_end = strlen($html); + $end = true; + } else { + $end = false; + } + $strlen_segment = $position_comment_end - $cursor; + $segment = substr($html, $cursor, $strlen_segment); + $token = new + HTMLPurifier_Token_Comment( + substr( + $segment, + 3, + $strlen_segment - 3 + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); + } + $array[] = $token; + $cursor = $end ? $position_comment_end : $position_comment_end + 3; + $inside_tag = false; + continue; + } + + // Check if it's an end tag + $is_end_tag = (strpos($segment, '/') === 0); + if ($is_end_tag) { + $type = substr($segment, 1); + $token = new HTMLPurifier_Token_End($type); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + $cursor = $position_next_gt + 1; + continue; + } + + // Check leading character is alnum, if not, we may + // have accidently grabbed an emoticon. Translate into + // text and go our merry way + if (!ctype_alpha($segment[0])) { + // XML: $segment[0] !== '_' && $segment[0] !== ':' + if ($e) { + $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + } + $token = new HTMLPurifier_Token_Text('<'); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + continue; + } + + // Check if it is explicitly self closing, if so, remove + // trailing slash. Remember, we could have a tag like
, so + // any later token processing scripts must convert improperly + // classified EmptyTags from StartTags. + $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1); + if ($is_self_closing) { + $strlen_segment--; + $segment = substr($segment, 0, $strlen_segment); + } + + // Check if there are any attributes + $position_first_space = strcspn($segment, $this->_whitespace); + + if ($position_first_space >= $strlen_segment) { + if ($is_self_closing) { + $token = new HTMLPurifier_Token_Empty($segment); + } else { + $token = new HTMLPurifier_Token_Start($segment); + } + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + $cursor = $position_next_gt + 1; + continue; + } + + // Grab out all the data + $type = substr($segment, 0, $position_first_space); + $attribute_string = + trim( + substr( + $segment, + $position_first_space + ) + ); + if ($attribute_string) { + $attr = $this->parseAttributeString( + $attribute_string, + $config, + $context + ); + } else { + $attr = array(); + } + + if ($is_self_closing) { + $token = new HTMLPurifier_Token_Empty($type, $attr); + } else { + $token = new HTMLPurifier_Token_Start($type, $attr); + } + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $cursor = $position_next_gt + 1; + $inside_tag = false; + continue; + } else { + // inside tag, but there's no ending > sign + if ($e) { + $e->send(E_WARNING, 'Lexer: Missing gt'); + } + $token = new + HTMLPurifier_Token_Text( + '<' . + $this->parseText( + substr($html, $cursor), $config + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } + // no cursor scroll? Hmm... + $array[] = $token; + break; + } + break; + } + + $context->destroy('CurrentLine'); + $context->destroy('CurrentCol'); + return $array; + } + + /** + * PHP 5.0.x compatible substr_count that implements offset and length + * @param string $haystack + * @param string $needle + * @param int $offset + * @param int $length + * @return int + */ + protected function substrCount($haystack, $needle, $offset, $length) + { + static $oldVersion; + if ($oldVersion === null) { + $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); + } + if ($oldVersion) { + $haystack = substr($haystack, $offset, $length); + return substr_count($haystack, $needle); + } else { + return substr_count($haystack, $needle, $offset, $length); + } + } + + /** + * Takes the inside of an HTML tag and makes an assoc array of attributes. + * + * @param string $string Inside of tag excluding name. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array Assoc array of attributes. + */ + public function parseAttributeString($string, $config, $context) + { + $string = (string)$string; // quick typecast + + if ($string == '') { + return array(); + } // no attributes + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + // let's see if we can abort as quickly as possible + // one equal sign, no spaces => one attribute + $num_equal = substr_count($string, '='); + $has_space = strpos($string, ' '); + if ($num_equal === 0 && !$has_space) { + // bool attribute + return array($string => $string); + } elseif ($num_equal === 1 && !$has_space) { + // only one attribute + list($key, $quoted_value) = explode('=', $string); + $quoted_value = trim($quoted_value); + if (!$key) { + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + return array(); + } + if (!$quoted_value) { + return array($key => ''); + } + $first_char = @$quoted_value[0]; + $last_char = @$quoted_value[strlen($quoted_value) - 1]; + + $same_quote = ($first_char == $last_char); + $open_quote = ($first_char == '"' || $first_char == "'"); + + if ($same_quote && $open_quote) { + // well behaved + $value = substr($quoted_value, 1, strlen($quoted_value) - 2); + } else { + // not well behaved + if ($open_quote) { + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing end quote'); + } + $value = substr($quoted_value, 1); + } else { + $value = $quoted_value; + } + } + if ($value === false) { + $value = ''; + } + return array($key => $this->parseAttr($value, $config)); + } + + // setup loop environment + $array = array(); // return assoc array of attributes + $cursor = 0; // current position in string (moves forward) + $size = strlen($string); // size of the string (stays the same) + + // if we have unquoted attributes, the parser expects a terminating + // space, so let's guarantee that there's always a terminating space. + $string .= ' '; + + $old_cursor = -1; + while ($cursor < $size) { + if ($old_cursor >= $cursor) { + throw new Exception("Infinite loop detected"); + } + $old_cursor = $cursor; + + $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); + // grab the key + + $key_begin = $cursor; //we're currently at the start of the key + + // scroll past all characters that are the key (not whitespace or =) + $cursor += strcspn($string, $this->_whitespace . '=', $cursor); + + $key_end = $cursor; // now at the end of the key + + $key = substr($string, $key_begin, $key_end - $key_begin); + + if (!$key) { + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop + continue; // empty key + } + + // scroll past all whitespace + $cursor += strspn($string, $this->_whitespace, $cursor); + + if ($cursor >= $size) { + $array[$key] = $key; + break; + } + + // if the next character is an equal sign, we've got a regular + // pair, otherwise, it's a bool attribute + $first_char = @$string[$cursor]; + + if ($first_char == '=') { + // key="value" + + $cursor++; + $cursor += strspn($string, $this->_whitespace, $cursor); + + if ($cursor === false) { + $array[$key] = ''; + break; + } + + // we might be in front of a quote right now + + $char = @$string[$cursor]; + + if ($char == '"' || $char == "'") { + // it's quoted, end bound is $char + $cursor++; + $value_begin = $cursor; + $cursor = strpos($string, $char, $cursor); + $value_end = $cursor; + } else { + // it's not quoted, end bound is whitespace + $value_begin = $cursor; + $cursor += strcspn($string, $this->_whitespace, $cursor); + $value_end = $cursor; + } + + // we reached a premature end + if ($cursor === false) { + $cursor = $size; + $value_end = $cursor; + } + + $value = substr($string, $value_begin, $value_end - $value_begin); + if ($value === false) { + $value = ''; + } + $array[$key] = $this->parseAttr($value, $config); + $cursor++; + } else { + // boolattr + if ($key !== '') { + $array[$key] = $key; + } else { + // purely theoretical + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + } + } + } + return $array; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php new file mode 100644 index 00000000000..72476ddf32b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php @@ -0,0 +1,4788 @@ +normalize($html, $config, $context); + $new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */); + try { + $parser = new HTML5($new_html); + $doc = $parser->save(); + } catch (DOMException $e) { + // Uh oh, it failed. Punt to DirectLex. + $lexer = new HTMLPurifier_Lexer_DirectLex(); + $context->register('PH5PError', $e); // save the error, so we can detect it + return $lexer->tokenizeHTML($html, $config, $context); // use original HTML + } + $tokens = array(); + $this->tokenizeDOM( + $doc->getElementsByTagName('html')->item(0)-> // + getElementsByTagName('body')->item(0) // + , + $tokens, $config + ); + return $tokens; + } +} + +/* + +Copyright 2007 Jeroen van der Meer + +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. + +*/ + +class HTML5 +{ + private $data; + private $char; + private $EOF; + private $state; + private $tree; + private $token; + private $content_model; + private $escape = false; + private $entities = array( + 'AElig;', + 'AElig', + 'AMP;', + 'AMP', + 'Aacute;', + 'Aacute', + 'Acirc;', + 'Acirc', + 'Agrave;', + 'Agrave', + 'Alpha;', + 'Aring;', + 'Aring', + 'Atilde;', + 'Atilde', + 'Auml;', + 'Auml', + 'Beta;', + 'COPY;', + 'COPY', + 'Ccedil;', + 'Ccedil', + 'Chi;', + 'Dagger;', + 'Delta;', + 'ETH;', + 'ETH', + 'Eacute;', + 'Eacute', + 'Ecirc;', + 'Ecirc', + 'Egrave;', + 'Egrave', + 'Epsilon;', + 'Eta;', + 'Euml;', + 'Euml', + 'GT;', + 'GT', + 'Gamma;', + 'Iacute;', + 'Iacute', + 'Icirc;', + 'Icirc', + 'Igrave;', + 'Igrave', + 'Iota;', + 'Iuml;', + 'Iuml', + 'Kappa;', + 'LT;', + 'LT', + 'Lambda;', + 'Mu;', + 'Ntilde;', + 'Ntilde', + 'Nu;', + 'OElig;', + 'Oacute;', + 'Oacute', + 'Ocirc;', + 'Ocirc', + 'Ograve;', + 'Ograve', + 'Omega;', + 'Omicron;', + 'Oslash;', + 'Oslash', + 'Otilde;', + 'Otilde', + 'Ouml;', + 'Ouml', + 'Phi;', + 'Pi;', + 'Prime;', + 'Psi;', + 'QUOT;', + 'QUOT', + 'REG;', + 'REG', + 'Rho;', + 'Scaron;', + 'Sigma;', + 'THORN;', + 'THORN', + 'TRADE;', + 'Tau;', + 'Theta;', + 'Uacute;', + 'Uacute', + 'Ucirc;', + 'Ucirc', + 'Ugrave;', + 'Ugrave', + 'Upsilon;', + 'Uuml;', + 'Uuml', + 'Xi;', + 'Yacute;', + 'Yacute', + 'Yuml;', + 'Zeta;', + 'aacute;', + 'aacute', + 'acirc;', + 'acirc', + 'acute;', + 'acute', + 'aelig;', + 'aelig', + 'agrave;', + 'agrave', + 'alefsym;', + 'alpha;', + 'amp;', + 'amp', + 'and;', + 'ang;', + 'apos;', + 'aring;', + 'aring', + 'asymp;', + 'atilde;', + 'atilde', + 'auml;', + 'auml', + 'bdquo;', + 'beta;', + 'brvbar;', + 'brvbar', + 'bull;', + 'cap;', + 'ccedil;', + 'ccedil', + 'cedil;', + 'cedil', + 'cent;', + 'cent', + 'chi;', + 'circ;', + 'clubs;', + 'cong;', + 'copy;', + 'copy', + 'crarr;', + 'cup;', + 'curren;', + 'curren', + 'dArr;', + 'dagger;', + 'darr;', + 'deg;', + 'deg', + 'delta;', + 'diams;', + 'divide;', + 'divide', + 'eacute;', + 'eacute', + 'ecirc;', + 'ecirc', + 'egrave;', + 'egrave', + 'empty;', + 'emsp;', + 'ensp;', + 'epsilon;', + 'equiv;', + 'eta;', + 'eth;', + 'eth', + 'euml;', + 'euml', + 'euro;', + 'exist;', + 'fnof;', + 'forall;', + 'frac12;', + 'frac12', + 'frac14;', + 'frac14', + 'frac34;', + 'frac34', + 'frasl;', + 'gamma;', + 'ge;', + 'gt;', + 'gt', + 'hArr;', + 'harr;', + 'hearts;', + 'hellip;', + 'iacute;', + 'iacute', + 'icirc;', + 'icirc', + 'iexcl;', + 'iexcl', + 'igrave;', + 'igrave', + 'image;', + 'infin;', + 'int;', + 'iota;', + 'iquest;', + 'iquest', + 'isin;', + 'iuml;', + 'iuml', + 'kappa;', + 'lArr;', + 'lambda;', + 'lang;', + 'laquo;', + 'laquo', + 'larr;', + 'lceil;', + 'ldquo;', + 'le;', + 'lfloor;', + 'lowast;', + 'loz;', + 'lrm;', + 'lsaquo;', + 'lsquo;', + 'lt;', + 'lt', + 'macr;', + 'macr', + 'mdash;', + 'micro;', + 'micro', + 'middot;', + 'middot', + 'minus;', + 'mu;', + 'nabla;', + 'nbsp;', + 'nbsp', + 'ndash;', + 'ne;', + 'ni;', + 'not;', + 'not', + 'notin;', + 'nsub;', + 'ntilde;', + 'ntilde', + 'nu;', + 'oacute;', + 'oacute', + 'ocirc;', + 'ocirc', + 'oelig;', + 'ograve;', + 'ograve', + 'oline;', + 'omega;', + 'omicron;', + 'oplus;', + 'or;', + 'ordf;', + 'ordf', + 'ordm;', + 'ordm', + 'oslash;', + 'oslash', + 'otilde;', + 'otilde', + 'otimes;', + 'ouml;', + 'ouml', + 'para;', + 'para', + 'part;', + 'permil;', + 'perp;', + 'phi;', + 'pi;', + 'piv;', + 'plusmn;', + 'plusmn', + 'pound;', + 'pound', + 'prime;', + 'prod;', + 'prop;', + 'psi;', + 'quot;', + 'quot', + 'rArr;', + 'radic;', + 'rang;', + 'raquo;', + 'raquo', + 'rarr;', + 'rceil;', + 'rdquo;', + 'real;', + 'reg;', + 'reg', + 'rfloor;', + 'rho;', + 'rlm;', + 'rsaquo;', + 'rsquo;', + 'sbquo;', + 'scaron;', + 'sdot;', + 'sect;', + 'sect', + 'shy;', + 'shy', + 'sigma;', + 'sigmaf;', + 'sim;', + 'spades;', + 'sub;', + 'sube;', + 'sum;', + 'sup1;', + 'sup1', + 'sup2;', + 'sup2', + 'sup3;', + 'sup3', + 'sup;', + 'supe;', + 'szlig;', + 'szlig', + 'tau;', + 'there4;', + 'theta;', + 'thetasym;', + 'thinsp;', + 'thorn;', + 'thorn', + 'tilde;', + 'times;', + 'times', + 'trade;', + 'uArr;', + 'uacute;', + 'uacute', + 'uarr;', + 'ucirc;', + 'ucirc', + 'ugrave;', + 'ugrave', + 'uml;', + 'uml', + 'upsih;', + 'upsilon;', + 'uuml;', + 'uuml', + 'weierp;', + 'xi;', + 'yacute;', + 'yacute', + 'yen;', + 'yen', + 'yuml;', + 'yuml', + 'zeta;', + 'zwj;', + 'zwnj;' + ); + + const PCDATA = 0; + const RCDATA = 1; + const CDATA = 2; + const PLAINTEXT = 3; + + const DOCTYPE = 0; + const STARTTAG = 1; + const ENDTAG = 2; + const COMMENT = 3; + const CHARACTR = 4; + const EOF = 5; + + public function __construct($data) + { + $this->data = $data; + $this->char = -1; + $this->EOF = strlen($data); + $this->tree = new HTML5TreeConstructer; + $this->content_model = self::PCDATA; + + $this->state = 'data'; + + while ($this->state !== null) { + $this->{$this->state . 'State'}(); + } + } + + public function save() + { + return $this->tree->save(); + } + + private function char() + { + return ($this->char < $this->EOF) + ? $this->data[$this->char] + : false; + } + + private function character($s, $l = 0) + { + if ($s + $l < $this->EOF) { + if ($l === 0) { + return $this->data[$s]; + } else { + return substr($this->data, $s, $l); + } + } + } + + private function characters($char_class, $start) + { + return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start)); + } + + private function dataState() + { + // Consume the next input character + $this->char++; + $char = $this->char(); + + if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { + /* U+0026 AMPERSAND (&) + When the content model flag is set to one of the PCDATA or RCDATA + states: switch to the entity data state. Otherwise: treat it as per + the "anything else" entry below. */ + $this->state = 'entityData'; + + } elseif ($char === '-') { + /* If the content model flag is set to either the RCDATA state or + the CDATA state, and the escape flag is false, and there are at + least three characters before this one in the input stream, and the + last four characters in the input stream, including this one, are + U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, + and U+002D HYPHEN-MINUS (""), + set the escape flag to false. */ + if (($this->content_model === self::RCDATA || + $this->content_model === self::CDATA) && $this->escape === true && + $this->character($this->char, 3) === '-->' + ) { + $this->escape = false; + } + + /* In any case, emit the input character as a character token. + Stay in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + } elseif ($this->char === $this->EOF) { + /* EOF + Emit an end-of-file token. */ + $this->EOF(); + + } elseif ($this->content_model === self::PLAINTEXT) { + /* When the content model flag is set to the PLAINTEXT state + THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of + the text and emit it as a character token. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => substr($this->data, $this->char) + ) + ); + + $this->EOF(); + + } else { + /* Anything else + THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that + otherwise would also be treated as a character token and emit it + as a single character token. Stay in the data state. */ + $len = strcspn($this->data, '<&', $this->char); + $char = substr($this->data, $this->char, $len); + $this->char += $len - 1; + + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + $this->state = 'data'; + } + } + + private function entityDataState() + { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, emit a U+0026 AMPERSAND character token. + // Otherwise, emit the character token that was returned. + $char = (!$entity) ? '&' : $entity; + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + // Finally, switch to the data state. + $this->state = 'data'; + } + + private function tagOpenState() + { + switch ($this->content_model) { + case self::RCDATA: + case self::CDATA: + /* If the next input character is a U+002F SOLIDUS (/) character, + consume it and switch to the close tag open state. If the next + input character is not a U+002F SOLIDUS (/) character, emit a + U+003C LESS-THAN SIGN character token and switch to the data + state to process the next input character. */ + if ($this->character($this->char + 1) === '/') { + $this->char++; + $this->state = 'closeTagOpen'; + + } else { + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<' + ) + ); + + $this->state = 'data'; + } + break; + + case self::PCDATA: + // If the content model flag is set to the PCDATA state + // Consume the next input character: + $this->char++; + $char = $this->char(); + + if ($char === '!') { + /* U+0021 EXCLAMATION MARK (!) + Switch to the markup declaration open state. */ + $this->state = 'markupDeclarationOpen'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Switch to the close tag open state. */ + $this->state = 'closeTagOpen'; + + } elseif (preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new start tag token, set its tag name to the lowercase + version of the input character (add 0x0020 to the character's code + point), then switch to the tag name state. (Don't emit the token + yet; further details will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::STARTTAG, + 'attr' => array() + ); + + $this->state = 'tagName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Emit a U+003C LESS-THAN SIGN character token and a + U+003E GREATER-THAN SIGN character token. Switch to the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<>' + ) + ); + + $this->state = 'data'; + + } elseif ($char === '?') { + /* U+003F QUESTION MARK (?) + Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + + } else { + /* Anything else + Parse error. Emit a U+003C LESS-THAN SIGN character token and + reconsume the current input character in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<' + ) + ); + + $this->char--; + $this->state = 'data'; + } + break; + } + } + + private function closeTagOpenState() + { + $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); + $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; + + if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && + (!$the_same || ($the_same && (!preg_match( + '/[\t\n\x0b\x0c >\/]/', + $this->character($this->char + 1 + strlen($next_node)) + ) || $this->EOF === $this->char))) + ) { + /* If the content model flag is set to the RCDATA or CDATA states then + examine the next few characters. If they do not match the tag name of + the last start tag token emitted (case insensitively), or if they do but + they are not immediately followed by one of the following characters: + * U+0009 CHARACTER TABULATION + * U+000A LINE FEED (LF) + * U+000B LINE TABULATION + * U+000C FORM FEED (FF) + * U+0020 SPACE + * U+003E GREATER-THAN SIGN (>) + * U+002F SOLIDUS (/) + * EOF + ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character + token, a U+002F SOLIDUS character token, and switch to the data state + to process the next input character. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => 'state = 'data'; + + } else { + /* Otherwise, if the content model flag is set to the PCDATA state, + or if the next few characters do match that tag name, consume the + next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new end tag token, set its tag name to the lowercase version + of the input character (add 0x0020 to the character's code point), then + switch to the tag name state. (Don't emit the token yet; further details + will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::ENDTAG + ); + + $this->state = 'tagName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Switch to the data state. */ + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F + SOLIDUS character token. Reconsume the EOF character in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => 'char--; + $this->state = 'data'; + + } else { + /* Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + } + } + } + + private function tagNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } else { + /* Anything else + Append the current input character to the current tag token's tag name. + Stay in the tag name state. */ + $this->token['name'] .= strtolower($char); + $this->state = 'tagName'; + } + } + + private function beforeAttributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Stay in the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function attributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif ($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's name. + Stay in the attribute name state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['name'] .= strtolower($char); + + $this->state = 'attributeName'; + } + } + + private function afterAttributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the after attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif ($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the + before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function beforeAttributeValueState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the attribute value (double-quoted) state. */ + $this->state = 'attributeValueDoubleQuoted'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the attribute value (unquoted) state and reconsume + this input character. */ + $this->char--; + $this->state = 'attributeValueUnquoted'; + + } elseif ($char === '\'') { + /* U+0027 APOSTROPHE (') + Switch to the attribute value (single-quoted) state. */ + $this->state = 'attributeValueSingleQuoted'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Switch to the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function attributeValueDoubleQuotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if ($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('double'); + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (double-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueDoubleQuoted'; + } + } + + private function attributeValueSingleQuotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if ($char === '\'') { + /* U+0022 QUOTATION MARK (') + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('single'); + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (single-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueSingleQuoted'; + } + } + + private function attributeValueUnquotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState(); + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function entityInAttributeValueState() + { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, append a U+0026 AMPERSAND character to the + // current attribute's value. Otherwise, emit the character token that + // was returned. + $char = (!$entity) + ? '&' + : $entity; + + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + } + + private function bogusCommentState() + { + /* Consume every character up to the first U+003E GREATER-THAN SIGN + character (>) or the end of the file (EOF), whichever comes first. Emit + a comment token whose data is the concatenation of all the characters + starting from and including the character that caused the state machine + to switch into the bogus comment state, up to and including the last + consumed character before the U+003E character, if any, or up to the + end of the file otherwise. (If the comment was started by the end of + the file (EOF), the token is empty.) */ + $data = $this->characters('^>', $this->char); + $this->emitToken( + array( + 'data' => $data, + 'type' => self::COMMENT + ) + ); + + $this->char += strlen($data); + + /* Switch to the data state. */ + $this->state = 'data'; + + /* If the end of the file was reached, reconsume the EOF character. */ + if ($this->char === $this->EOF) { + $this->char = $this->EOF - 1; + } + } + + private function markupDeclarationOpenState() + { + /* If the next two characters are both U+002D HYPHEN-MINUS (-) + characters, consume those two characters, create a comment token whose + data is the empty string, and switch to the comment state. */ + if ($this->character($this->char + 1, 2) === '--') { + $this->char += 2; + $this->state = 'comment'; + $this->token = array( + 'data' => null, + 'type' => self::COMMENT + ); + + /* Otherwise if the next seven chacacters are a case-insensitive match + for the word "DOCTYPE", then consume those characters and switch to the + DOCTYPE state. */ + } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') { + $this->char += 7; + $this->state = 'doctype'; + + /* Otherwise, is is a parse error. Switch to the bogus comment state. + The next character that is consumed, if any, is the first character + that will be in the comment. */ + } else { + $this->char++; + $this->state = 'bogusComment'; + } + } + + private function commentState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if ($char === '-') { + /* Switch to the comment dash state */ + $this->state = 'commentDash'; + + /* EOF */ + } elseif ($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append the input character to the comment token's data. Stay in + the comment state. */ + $this->token['data'] .= $char; + } + } + + private function commentDashState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if ($char === '-') { + /* Switch to the comment end state */ + $this->state = 'commentEnd'; + + /* EOF */ + } elseif ($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append a U+002D HYPHEN-MINUS (-) character and the input + character to the comment token's data. Switch to the comment state. */ + $this->token['data'] .= '-' . $char; + $this->state = 'comment'; + } + } + + private function commentEndState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '-') { + $this->token['data'] .= '-'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['data'] .= '--' . $char; + $this->state = 'comment'; + } + } + + private function doctypeState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'beforeDoctypeName'; + + } else { + $this->char--; + $this->state = 'beforeDoctypeName'; + } + } + + private function beforeDoctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the before DOCTYPE name state. + + } elseif (preg_match('/^[a-z]$/', $char)) { + $this->token = array( + 'name' => strtoupper($char), + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + + } elseif ($char === '>') { + $this->emitToken( + array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + ) + ); + + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken( + array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + ) + ); + + $this->char--; + $this->state = 'data'; + + } else { + $this->token = array( + 'name' => $char, + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + } + } + + private function doctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'AfterDoctypeName'; + + } elseif ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif (preg_match('/^[a-z]$/', $char)) { + $this->token['name'] .= strtoupper($char); + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['name'] .= $char; + } + + $this->token['error'] = ($this->token['name'] === 'HTML') + ? false + : true; + } + + private function afterDoctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the DOCTYPE name state. + + } elseif ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['error'] = true; + $this->state = 'bogusDoctype'; + } + } + + private function bogusDoctypeState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + // Stay in the bogus DOCTYPE state. + } + } + + private function entity() + { + $start = $this->char; + + // This section defines how to consume an entity. This definition is + // used when parsing entities in text and in attributes. + + // The behaviour depends on the identity of the next character (the + // one immediately after the U+0026 AMPERSAND character): + + switch ($this->character($this->char + 1)) { + // U+0023 NUMBER SIGN (#) + case '#': + + // The behaviour further depends on the character after the + // U+0023 NUMBER SIGN: + switch ($this->character($this->char + 1)) { + // U+0078 LATIN SMALL LETTER X + // U+0058 LATIN CAPITAL LETTER X + case 'x': + case 'X': + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 + // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER + // A, through to U+0046 LATIN CAPITAL LETTER F (in other + // words, 0-9, A-F, a-f). + $char = 1; + $char_class = '0-9A-Fa-f'; + break; + + // Anything else + default: + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE (i.e. just 0-9). + $char = 0; + $char_class = '0-9'; + break; + } + + // Consume as many characters as match the range of characters + // given above. + $this->char++; + $e_name = $this->characters($char_class, $this->char + $char + 1); + $entity = $this->character($start, $this->char); + $cond = strlen($e_name) > 0; + + // The rest of the parsing happens below. + break; + + // Anything else + default: + // Consume the maximum number of characters possible, with the + // consumed characters case-sensitively matching one of the + // identifiers in the first column of the entities table. + + $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); + $len = strlen($e_name); + + for ($c = 1; $c <= $len; $c++) { + $id = substr($e_name, 0, $c); + $this->char++; + + if (in_array($id, $this->entities)) { + if ($e_name[$c - 1] !== ';') { + if ($c < $len && $e_name[$c] == ';') { + $this->char++; // consume extra semicolon + } + } + $entity = $id; + break; + } + } + + $cond = isset($entity); + // The rest of the parsing happens below. + break; + } + + if (!$cond) { + // If no match can be made, then this is a parse error. No + // characters are consumed, and nothing is returned. + $this->char = $start; + return false; + } + + // Return a character token for the character corresponding to the + // entity name (as given by the second column of the entities table). + return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8'); + } + + private function emitToken($token) + { + $emit = $this->tree->emitToken($token); + + if (is_int($emit)) { + $this->content_model = $emit; + + } elseif ($token['type'] === self::ENDTAG) { + $this->content_model = self::PCDATA; + } + } + + private function EOF() + { + $this->state = null; + $this->tree->emitToken( + array( + 'type' => self::EOF + ) + ); + } +} + +class HTML5TreeConstructer +{ + public $stack = array(); + + private $phase; + private $mode; + private $dom; + private $foster_parent = null; + private $a_formatting = array(); + + private $head_pointer = null; + private $form_pointer = null; + + private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th'); + private $formatting = array( + 'a', + 'b', + 'big', + 'em', + 'font', + 'i', + 'nobr', + 's', + 'small', + 'strike', + 'strong', + 'tt', + 'u' + ); + private $special = array( + 'address', + 'area', + 'base', + 'basefont', + 'bgsound', + 'blockquote', + 'body', + 'br', + 'center', + 'col', + 'colgroup', + 'dd', + 'dir', + 'div', + 'dl', + 'dt', + 'embed', + 'fieldset', + 'form', + 'frame', + 'frameset', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'hr', + 'iframe', + 'image', + 'img', + 'input', + 'isindex', + 'li', + 'link', + 'listing', + 'menu', + 'meta', + 'noembed', + 'noframes', + 'noscript', + 'ol', + 'optgroup', + 'option', + 'p', + 'param', + 'plaintext', + 'pre', + 'script', + 'select', + 'spacer', + 'style', + 'tbody', + 'textarea', + 'tfoot', + 'thead', + 'title', + 'tr', + 'ul', + 'wbr' + ); + + // The different phases. + const INIT_PHASE = 0; + const ROOT_PHASE = 1; + const MAIN_PHASE = 2; + const END_PHASE = 3; + + // The different insertion modes for the main phase. + const BEFOR_HEAD = 0; + const IN_HEAD = 1; + const AFTER_HEAD = 2; + const IN_BODY = 3; + const IN_TABLE = 4; + const IN_CAPTION = 5; + const IN_CGROUP = 6; + const IN_TBODY = 7; + const IN_ROW = 8; + const IN_CELL = 9; + const IN_SELECT = 10; + const AFTER_BODY = 11; + const IN_FRAME = 12; + const AFTR_FRAME = 13; + + // The different types of elements. + const SPECIAL = 0; + const SCOPING = 1; + const FORMATTING = 2; + const PHRASING = 3; + + const MARKER = 0; + + public function __construct() + { + $this->phase = self::INIT_PHASE; + $this->mode = self::BEFOR_HEAD; + $this->dom = new DOMDocument; + + $this->dom->encoding = 'UTF-8'; + $this->dom->preserveWhiteSpace = true; + $this->dom->substituteEntities = true; + $this->dom->strictErrorChecking = false; + } + + // Process tag tokens + public function emitToken($token) + { + switch ($this->phase) { + case self::INIT_PHASE: + return $this->initPhase($token); + break; + case self::ROOT_PHASE: + return $this->rootElementPhase($token); + break; + case self::MAIN_PHASE: + return $this->mainPhase($token); + break; + case self::END_PHASE : + return $this->trailingEndPhase($token); + break; + } + } + + private function initPhase($token) + { + /* Initially, the tree construction stage must handle each token + emitted from the tokenisation stage as follows: */ + + /* A DOCTYPE token that is marked as being in error + A comment token + A start tag token + An end tag token + A character token that is not one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE + An end-of-file token */ + if ((isset($token['error']) && $token['error']) || + $token['type'] === HTML5::COMMENT || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF || + ($token['type'] === HTML5::CHARACTR && isset($token['data']) && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) + ) { + /* This specification does not define how to handle this case. In + particular, user agents may ignore the entirety of this specification + altogether for such documents, and instead invoke special parse modes + with a greater emphasis on backwards compatibility. */ + + $this->phase = self::ROOT_PHASE; + return $this->rootElementPhase($token); + + /* A DOCTYPE token marked as being correct */ + } elseif (isset($token['error']) && !$token['error']) { + /* Append a DocumentType node to the Document node, with the name + attribute set to the name given in the DOCTYPE token (which will be + "HTML"), and the other attributes specific to DocumentType objects + set to null, empty lists, or the empty string as appropriate. */ + $doctype = new DOMDocumentType(null, null, 'HTML'); + + /* Then, switch to the root element phase of the tree construction + stage. */ + $this->phase = self::ROOT_PHASE; + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif (isset($token['data']) && preg_match( + '/^[\t\n\x0b\x0c ]+$/', + $token['data'] + ) + ) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + } + } + + private function rootElementPhase($token) + { + /* After the initial phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED + (FF), or U+0020 SPACE + A start tag token + An end tag token + An end-of-file token */ + } elseif (($token['type'] === HTML5::CHARACTR && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF + ) { + /* Create an HTMLElement node with the tag name html, in the HTML + namespace. Append it to the Document object. Switch to the main + phase and reprocess the current token. */ + $html = $this->dom->createElement('html'); + $this->dom->appendChild($html); + $this->stack[] = $html; + + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + } + } + + private function mainPhase($token) + { + /* Tokens in the main phase must be handled as follows: */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A start tag token with the tag name "html" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { + /* If this start tag token was not the first start tag token, then + it is a parse error. */ + + /* For each attribute on the token, check to see if the attribute + is already present on the top element of the stack of open elements. + If it is not, add the attribute and its corresponding value to that + element. */ + foreach ($token['attr'] as $attr) { + if (!$this->stack[0]->hasAttribute($attr['name'])) { + $this->stack[0]->setAttribute($attr['name'], $attr['value']); + } + } + + /* An end-of-file token */ + } elseif ($token['type'] === HTML5::EOF) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Anything else. */ + } else { + /* Depends on the insertion mode: */ + switch ($this->mode) { + case self::BEFOR_HEAD: + return $this->beforeHead($token); + break; + case self::IN_HEAD: + return $this->inHead($token); + break; + case self::AFTER_HEAD: + return $this->afterHead($token); + break; + case self::IN_BODY: + return $this->inBody($token); + break; + case self::IN_TABLE: + return $this->inTable($token); + break; + case self::IN_CAPTION: + return $this->inCaption($token); + break; + case self::IN_CGROUP: + return $this->inColumnGroup($token); + break; + case self::IN_TBODY: + return $this->inTableBody($token); + break; + case self::IN_ROW: + return $this->inRow($token); + break; + case self::IN_CELL: + return $this->inCell($token); + break; + case self::IN_SELECT: + return $this->inSelect($token); + break; + case self::AFTER_BODY: + return $this->afterBody($token); + break; + case self::IN_FRAME: + return $this->inFrameset($token); + break; + case self::AFTR_FRAME: + return $this->afterFrameset($token); + break; + case self::END_PHASE: + return $this->trailingEndPhase($token); + break; + } + } + } + + private function beforeHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "head" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { + /* Create an element for the token, append the new element to the + current node and push it onto the stack of open elements. */ + $element = $this->insertElement($token); + + /* Set the head element pointer to this new element node. */ + $this->head_pointer = $element; + + /* Change the insertion mode to "in head". */ + $this->mode = self::IN_HEAD; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title". Or an end tag with the tag name "html". + Or a character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or any other start tag token */ + } elseif ($token['type'] === HTML5::STARTTAG || + ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || + ($token['type'] === HTML5::CHARACTR && !preg_match( + '/^[\t\n\x0b\x0c ]$/', + $token['data'] + )) + ) { + /* Act as if a start tag token with the tag name "head" and no + attributes had been seen, then reprocess the current token. */ + $this->beforeHead( + array( + 'name' => 'head', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inHead($token); + + /* Any other end tag */ + } elseif ($token['type'] === HTML5::ENDTAG) { + /* Parse error. Ignore the token. */ + } + } + + private function inHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. + + THIS DIFFERS FROM THE SPEC: If the current node is either a title, style + or script element, append the character to the current node regardless + of its content. */ + if (($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( + $token['type'] === HTML5::CHARACTR && in_array( + end($this->stack)->nodeName, + array('title', 'style', 'script') + )) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('title', 'style', 'script')) + ) { + array_pop($this->stack); + return HTML5::PCDATA; + + /* A start tag with the tag name "title" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $element = $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the RCDATA state. */ + return HTML5::RCDATA; + + /* A start tag with the tag name "style" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "script" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { + /* Create an element for the token. */ + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "base", "link", or "meta" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('base', 'link', 'meta') + ) + ) { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + array_pop($this->stack); + + } else { + $this->insertElement($token); + } + + /* An end tag with the tag name "head" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { + /* If the current node is a head element, pop the current node off + the stack of open elements. */ + if ($this->head_pointer->isSameNode(end($this->stack))) { + array_pop($this->stack); + + /* Otherwise, this is a parse error. */ + } else { + // k + } + + /* Change the insertion mode to "after head". */ + $this->mode = self::AFTER_HEAD; + + /* A start tag with the tag name "head" or an end tag except "html". */ + } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || + ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html') + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* If the current node is a head element, act as if an end tag + token with the tag name "head" had been seen. */ + if ($this->head_pointer->isSameNode(end($this->stack))) { + $this->inHead( + array( + 'name' => 'head', + 'type' => HTML5::ENDTAG + ) + ); + + /* Otherwise, change the insertion mode to "after head". */ + } else { + $this->mode = self::AFTER_HEAD; + } + + /* Then, reprocess the current token. */ + return $this->afterHead($token); + } + } + + private function afterHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "body" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { + /* Insert a body element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in body". */ + $this->mode = self::IN_BODY; + + /* A start tag token with the tag name "frameset" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { + /* Insert a frameset element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in frameset". */ + $this->mode = self::IN_FRAME; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('base', 'link', 'meta', 'script', 'style', 'title') + ) + ) { + /* Parse error. Switch the insertion mode back to "in head" and + reprocess the token. */ + $this->mode = self::IN_HEAD; + return $this->inHead($token); + + /* Anything else */ + } else { + /* Act as if a start tag token with the tag name "body" and no + attributes had been seen, and then reprocess the current token. */ + $this->afterHead( + array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inBody($token); + } + } + + private function inBody($token) + { + /* Handle the token as follows: */ + + switch ($token['type']) { + /* A character token */ + case HTML5::CHARACTR: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + break; + + /* A comment token */ + case HTML5::COMMENT: + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + break; + + case HTML5::STARTTAG: + switch ($token['name']) { + /* A start tag token whose tag name is one of: "script", + "style" */ + case 'script': + case 'style': + /* Process the token as if the insertion mode had been "in + head". */ + return $this->inHead($token); + break; + + /* A start tag token whose tag name is one of: "base", "link", + "meta", "title" */ + case 'base': + case 'link': + case 'meta': + case 'title': + /* Parse error. Process the token as if the insertion mode + had been "in head". */ + return $this->inHead($token); + break; + + /* A start tag token with the tag name "body" */ + case 'body': + /* Parse error. If the second element on the stack of open + elements is not a body element, or, if the stack of open + elements has only one node on it, then ignore the token. + (innerHTML case) */ + if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { + // Ignore + + /* Otherwise, for each attribute on the token, check to see + if the attribute is already present on the body element (the + second element) on the stack of open elements. If it is not, + add the attribute and its corresponding value to that + element. */ + } else { + foreach ($token['attr'] as $attr) { + if (!$this->stack[1]->hasAttribute($attr['name'])) { + $this->stack[1]->setAttribute($attr['name'], $attr['value']); + } + } + } + break; + + /* A start tag whose tag name is one of: "address", + "blockquote", "center", "dir", "div", "dl", "fieldset", + "listing", "menu", "ol", "p", "ul" */ + case 'address': + case 'blockquote': + case 'center': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'listing': + case 'menu': + case 'ol': + case 'p': + case 'ul': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "form" */ + case 'form': + /* If the form element pointer is not null, ignore the + token with a parse error. */ + if ($this->form_pointer !== null) { + // Ignore. + + /* Otherwise: */ + } else { + /* If the stack of open elements has a p element in + scope, then act as if an end tag with the tag name p + had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token, and set the + form element pointer to point to the element created. */ + $element = $this->insertElement($token); + $this->form_pointer = $element; + } + break; + + /* A start tag whose tag name is "li", "dd" or "dt" */ + case 'li': + case 'dd': + case 'dt': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + $stack_length = count($this->stack) - 1; + + for ($n = $stack_length; 0 <= $n; $n--) { + /* 1. Initialise node to be the current node (the + bottommost node of the stack). */ + $stop = false; + $node = $this->stack[$n]; + $cat = $this->getElementCategory($node->tagName); + + /* 2. If node is an li, dd or dt element, then pop all + the nodes from the current node up to node, including + node, then stop this algorithm. */ + if ($token['name'] === $node->tagName || ($token['name'] !== 'li' + && ($node->tagName === 'dd' || $node->tagName === 'dt')) + ) { + for ($x = $stack_length; $x >= $n; $x--) { + array_pop($this->stack); + } + + break; + } + + /* 3. If node is not in the formatting category, and is + not in the phrasing category, and is not an address or + div element, then stop this algorithm. */ + if ($cat !== self::FORMATTING && $cat !== self::PHRASING && + $node->tagName !== 'address' && $node->tagName !== 'div' + ) { + break; + } + } + + /* Finally, insert an HTML element with the same tag + name as the token's. */ + $this->insertElement($token); + break; + + /* A start tag token whose tag name is "plaintext" */ + case 'plaintext': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + return HTML5::PLAINTEXT; + break; + + /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + this is a parse error; pop elements from the stack until an + element with one of those tag names has been popped from the + stack. */ + while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { + array_pop($this->stack); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "a" */ + case 'a': + /* If the list of active formatting elements contains + an element whose tag name is "a" between the end of the + list and the last marker on the list (or the start of + the list if there is no marker on the list), then this + is a parse error; act as if an end tag with the tag name + "a" had been seen, then remove that element from the list + of active formatting elements and the stack of open + elements if the end tag didn't already remove it (it + might not have if the element is not in table scope). */ + $leng = count($this->a_formatting); + + for ($n = $leng - 1; $n >= 0; $n--) { + if ($this->a_formatting[$n] === self::MARKER) { + break; + + } elseif ($this->a_formatting[$n]->nodeName === 'a') { + $this->emitToken( + array( + 'name' => 'a', + 'type' => HTML5::ENDTAG + ) + ); + break; + } + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag whose tag name is one of: "b", "big", "em", "font", + "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'b': + case 'big': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag token whose tag name is "button" */ + case 'button': + /* If the stack of open elements has a button element in scope, + then this is a parse error; act as if an end tag with the tag + name "button" had been seen, then reprocess the token. (We don't + do that. Unnecessary.) */ + if ($this->elementInScope('button')) { + $this->inBody( + array( + 'name' => 'button', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is one of: "marquee", "object" */ + case 'marquee': + case 'object': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is "xmp" */ + case 'xmp': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Switch the content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "table" */ + case 'table': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + break; + + /* A start tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ + case 'area': + case 'basefont': + case 'bgsound': + case 'br': + case 'embed': + case 'img': + case 'param': + case 'spacer': + case 'wbr': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "hr" */ + case 'hr': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "image" */ + case 'image': + /* Parse error. Change the token's tag name to "img" and + reprocess it. (Don't ask.) */ + $token['name'] = 'img'; + return $this->inBody($token); + break; + + /* A start tag whose tag name is "input" */ + case 'input': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an input element for the token. */ + $element = $this->insertElement($token, false); + + /* If the form element pointer is not null, then associate the + input element with the form element pointed to by the form + element pointer. */ + $this->form_pointer !== null + ? $this->form_pointer->appendChild($element) + : end($this->stack)->appendChild($element); + + /* Pop that input element off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "isindex" */ + case 'isindex': + /* Parse error. */ + // w/e + + /* If the form element pointer is not null, + then ignore the token. */ + if ($this->form_pointer === null) { + /* Act as if a start tag token with the tag name "form" had + been seen. */ + $this->inBody( + array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody( + array( + 'name' => 'hr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "p" had + been seen. */ + $this->inBody( + array( + 'name' => 'p', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "label" + had been seen. */ + $this->inBody( + array( + 'name' => 'label', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a stream of character tokens had been seen. */ + $this->insertText( + 'This is a searchable index. ' . + 'Insert your search keywords here: ' + ); + + /* Act as if a start tag token with the tag name "input" + had been seen, with all the attributes from the "isindex" + token, except with the "name" attribute set to the value + "isindex" (ignoring any explicit "name" attribute). */ + $attr = $token['attr']; + $attr[] = array('name' => 'name', 'value' => 'isindex'); + + $this->inBody( + array( + 'name' => 'input', + 'type' => HTML5::STARTTAG, + 'attr' => $attr + ) + ); + + /* Act as if a stream of character tokens had been seen + (see below for what they should say). */ + $this->insertText( + 'This is a searchable index. ' . + 'Insert your search keywords here: ' + ); + + /* Act as if an end tag token with the tag name "label" + had been seen. */ + $this->inBody( + array( + 'name' => 'label', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if an end tag token with the tag name "p" had + been seen. */ + $this->inBody( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody( + array( + 'name' => 'hr', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if an end tag token with the tag name "form" had + been seen. */ + $this->inBody( + array( + 'name' => 'form', + 'type' => HTML5::ENDTAG + ) + ); + } + break; + + /* A start tag whose tag name is "textarea" */ + case 'textarea': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the + RCDATA state. */ + return HTML5::RCDATA; + break; + + /* A start tag whose tag name is one of: "iframe", "noembed", + "noframes" */ + case 'iframe': + case 'noembed': + case 'noframes': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "select" */ + case 'select': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in select". */ + $this->mode = self::IN_SELECT; + break; + + /* A start or end tag whose tag name is one of: "caption", "col", + "colgroup", "frame", "frameset", "head", "option", "optgroup", + "tbody", "td", "tfoot", "th", "thead", "tr". */ + case 'caption': + case 'col': + case 'colgroup': + case 'frame': + case 'frameset': + case 'head': + case 'option': + case 'optgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + // Parse error. Ignore the token. + break; + + /* A start or end tag whose tag name is one of: "event-source", + "section", "nav", "article", "aside", "header", "footer", + "datagrid", "command" */ + case 'event-source': + case 'section': + case 'nav': + case 'article': + case 'aside': + case 'header': + case 'footer': + case 'datagrid': + case 'command': + // Work in progress! + break; + + /* A start tag token not covered by the previous entries */ + default: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + $this->insertElement($token, true, true); + break; + } + break; + + case HTML5::ENDTAG: + switch ($token['name']) { + /* An end tag with the tag name "body" */ + case 'body': + /* If the second element in the stack of open elements is + not a body element, this is a parse error. Ignore the token. + (innerHTML case) */ + if (count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { + // Ignore. + + /* If the current node is not the body element, then this + is a parse error. */ + } elseif (end($this->stack)->nodeName !== 'body') { + // Parse error. + } + + /* Change the insertion mode to "after body". */ + $this->mode = self::AFTER_BODY; + break; + + /* An end tag with the tag name "html" */ + case 'html': + /* Act as if an end tag with tag name "body" had been seen, + then, if that token wasn't ignored, reprocess the current + token. */ + $this->inBody( + array( + 'name' => 'body', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->afterBody($token); + break; + + /* An end tag whose tag name is one of: "address", "blockquote", + "center", "dir", "div", "dl", "fieldset", "listing", "menu", + "ol", "pre", "ul" */ + case 'address': + case 'blockquote': + case 'center': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'listing': + case 'menu': + case 'ol': + case 'pre': + case 'ul': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with + the same tag name as that of the token, then this + is a parse error. */ + // w/e + + /* If the stack of open elements has an element in + scope with the same tag name as that of the token, + then pop elements from this stack until an element + with that tag name has been popped from the stack. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is "form" */ + case 'form': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + } + + if (end($this->stack)->nodeName !== $token['name']) { + /* Now, if the current node is not an element with the + same tag name as that of the token, then this is a parse + error. */ + // w/e + + } else { + /* Otherwise, if the current node is an element with + the same tag name as that of the token pop that element + from the stack. */ + array_pop($this->stack); + } + + /* In any case, set the form element pointer to null. */ + $this->form_pointer = null; + break; + + /* An end tag whose tag name is "p" */ + case 'p': + /* If the stack of open elements has a p element in scope, + then generate implied end tags, except for p elements. */ + if ($this->elementInScope('p')) { + $this->generateImpliedEndTags(array('p')); + + /* If the current node is not a p element, then this is + a parse error. */ + // k + + /* If the stack of open elements has a p element in + scope, then pop elements from this stack until the stack + no longer has a p element in scope. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->elementInScope('p')) { + array_pop($this->stack); + + } else { + break; + } + } + } + break; + + /* An end tag whose tag name is "dd", "dt", or "li" */ + case 'dd': + case 'dt': + case 'li': + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + generate implied end tags, except for elements with the + same tag name as the token. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(array($token['name'])); + + /* If the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + pop elements from this stack until an element with that + tag name has been popped from the stack. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + generate implied end tags. */ + if ($this->elementInScope($elements)) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as that of the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has in scope an element + whose tag name is one of "h1", "h2", "h3", "h4", "h5", or + "h6", then pop elements from the stack until an element + with one of those tag names has been popped from the stack. */ + while ($this->elementInScope($elements)) { + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "a", "b", "big", "em", + "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'a': + case 'b': + case 'big': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + /* 1. Let the formatting element be the last element in + the list of active formatting elements that: + * is between the end of the list and the last scope + marker in the list, if any, or the start of the list + otherwise, and + * has the same tag name as the token. + */ + while (true) { + for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) { + if ($this->a_formatting[$a] === self::MARKER) { + break; + + } elseif ($this->a_formatting[$a]->tagName === $token['name']) { + $formatting_element = $this->a_formatting[$a]; + $in_stack = in_array($formatting_element, $this->stack, true); + $fe_af_pos = $a; + break; + } + } + + /* If there is no such node, or, if that node is + also in the stack of open elements but the element + is not in scope, then this is a parse error. Abort + these steps. The token is ignored. */ + if (!isset($formatting_element) || ($in_stack && + !$this->elementInScope($token['name'])) + ) { + break; + + /* Otherwise, if there is such a node, but that node + is not in the stack of open elements, then this is a + parse error; remove the element from the list, and + abort these steps. */ + } elseif (isset($formatting_element) && !$in_stack) { + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 2. Let the furthest block be the topmost node in the + stack of open elements that is lower in the stack + than the formatting element, and is not an element in + the phrasing or formatting categories. There might + not be one. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $length = count($this->stack); + + for ($s = $fe_s_pos + 1; $s < $length; $s++) { + $category = $this->getElementCategory($this->stack[$s]->nodeName); + + if ($category !== self::PHRASING && $category !== self::FORMATTING) { + $furthest_block = $this->stack[$s]; + } + } + + /* 3. If there is no furthest block, then the UA must + skip the subsequent steps and instead just pop all + the nodes from the bottom of the stack of open + elements, from the current node up to the formatting + element, and remove the formatting element from the + list of active formatting elements. */ + if (!isset($furthest_block)) { + for ($n = $length - 1; $n >= $fe_s_pos; $n--) { + array_pop($this->stack); + } + + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 4. Let the common ancestor be the element + immediately above the formatting element in the stack + of open elements. */ + $common_ancestor = $this->stack[$fe_s_pos - 1]; + + /* 5. If the furthest block has a parent node, then + remove the furthest block from its parent node. */ + if ($furthest_block->parentNode !== null) { + $furthest_block->parentNode->removeChild($furthest_block); + } + + /* 6. Let a bookmark note the position of the + formatting element in the list of active formatting + elements relative to the elements on either side + of it in the list. */ + $bookmark = $fe_af_pos; + + /* 7. Let node and last node be the furthest block. + Follow these steps: */ + $node = $furthest_block; + $last_node = $furthest_block; + + while (true) { + for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { + /* 7.1 Let node be the element immediately + prior to node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 7.2 If node is not in the list of active + formatting elements, then remove node from + the stack of open elements and then go back + to step 1. */ + if (!in_array($node, $this->a_formatting, true)) { + unset($this->stack[$n]); + $this->stack = array_merge($this->stack); + + } else { + break; + } + } + + /* 7.3 Otherwise, if node is the formatting + element, then go to the next step in the overall + algorithm. */ + if ($node === $formatting_element) { + break; + + /* 7.4 Otherwise, if last node is the furthest + block, then move the aforementioned bookmark to + be immediately after the node in the list of + active formatting elements. */ + } elseif ($last_node === $furthest_block) { + $bookmark = array_search($node, $this->a_formatting, true) + 1; + } + + /* 7.5 If node has any children, perform a + shallow clone of node, replace the entry for + node in the list of active formatting elements + with an entry for the clone, replace the entry + for node in the stack of open elements with an + entry for the clone, and let node be the clone. */ + if ($node->hasChildNodes()) { + $clone = $node->cloneNode(); + $s_pos = array_search($node, $this->stack, true); + $a_pos = array_search($node, $this->a_formatting, true); + + $this->stack[$s_pos] = $clone; + $this->a_formatting[$a_pos] = $clone; + $node = $clone; + } + + /* 7.6 Insert last node into node, first removing + it from its previous parent node if any. */ + if ($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $node->appendChild($last_node); + + /* 7.7 Let last node be node. */ + $last_node = $node; + } + + /* 8. Insert whatever last node ended up being in + the previous step into the common ancestor node, + first removing it from its previous parent node if + any. */ + if ($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $common_ancestor->appendChild($last_node); + + /* 9. Perform a shallow clone of the formatting + element. */ + $clone = $formatting_element->cloneNode(); + + /* 10. Take all of the child nodes of the furthest + block and append them to the clone created in the + last step. */ + while ($furthest_block->hasChildNodes()) { + $child = $furthest_block->firstChild; + $furthest_block->removeChild($child); + $clone->appendChild($child); + } + + /* 11. Append that clone to the furthest block. */ + $furthest_block->appendChild($clone); + + /* 12. Remove the formatting element from the list + of active formatting elements, and insert the clone + into the list of active formatting elements at the + position of the aforementioned bookmark. */ + $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + + $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); + $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); + $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); + + /* 13. Remove the formatting element from the stack + of open elements, and insert the clone into the stack + of open elements immediately after (i.e. in a more + deeply nested position than) the position of the + furthest block in that stack. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $fb_s_pos = array_search($furthest_block, $this->stack, true); + unset($this->stack[$fe_s_pos]); + + $s_part1 = array_slice($this->stack, 0, $fb_s_pos); + $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); + $this->stack = array_merge($s_part1, array($clone), $s_part2); + + /* 14. Jump back to step 1 in this series of steps. */ + unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); + } + break; + + /* An end tag token whose tag name is one of: "button", + "marquee", "object" */ + case 'button': + case 'marquee': + case 'object': + /* If the stack of open elements has an element in scope whose + tag name matches the tag name of the token, then generate implied + tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // k + + /* Now, if the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then pop + elements from the stack until that element has been popped from + the stack, and clear the list of active formatting elements up + to the last marker. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + + $marker = end(array_keys($this->a_formatting, self::MARKER, true)); + + for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) { + array_pop($this->a_formatting); + } + } + break; + + /* Or an end tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "hr", "iframe", "image", "img", + "input", "isindex", "noembed", "noframes", "param", "select", + "spacer", "table", "textarea", "wbr" */ + case 'area': + case 'basefont': + case 'bgsound': + case 'br': + case 'embed': + case 'hr': + case 'iframe': + case 'image': + case 'img': + case 'input': + case 'isindex': + case 'noembed': + case 'noframes': + case 'param': + case 'select': + case 'spacer': + case 'table': + case 'textarea': + case 'wbr': + // Parse error. Ignore the token. + break; + + /* An end tag token not covered by the previous entries */ + default: + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + /* Initialise node to be the current node (the bottommost + node of the stack). */ + $node = end($this->stack); + + /* If node has the same tag name as the end tag token, + then: */ + if ($token['name'] === $node->nodeName) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* If the tag name of the end tag token does not + match the tag name of the current node, this is a + parse error. */ + // k + + /* Pop all the nodes from the current node up to + node, including node, then stop this algorithm. */ + for ($x = count($this->stack) - $n; $x >= $n; $x--) { + array_pop($this->stack); + } + + } else { + $category = $this->getElementCategory($node); + + if ($category !== self::SPECIAL && $category !== self::SCOPING) { + /* Otherwise, if node is in neither the formatting + category nor the phrasing category, then this is a + parse error. Stop this algorithm. The end tag token + is ignored. */ + return false; + } + } + } + break; + } + break; + } + } + + private function inTable($token) + { + $clear = array('html', 'table'); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "caption" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'caption' + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + + /* Insert an HTML element for the token, then switch the + insertion mode to "in caption". */ + $this->insertElement($token); + $this->mode = self::IN_CAPTION; + + /* A start tag whose tag name is "colgroup" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'colgroup' + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the + insertion mode to "in column group". */ + $this->insertElement($token); + $this->mode = self::IN_CGROUP; + + /* A start tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'col' + ) { + $this->inTable( + array( + 'name' => 'colgroup', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + $this->inColumnGroup($token); + + /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('tbody', 'tfoot', 'thead') + ) + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in table body". */ + $this->insertElement($token); + $this->mode = self::IN_TBODY; + + /* A start tag whose tag name is one of: "td", "th", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && + in_array($token['name'], array('td', 'th', 'tr')) + ) { + /* Act as if a start tag token with the tag name "tbody" had been + seen, then reprocess the current token. */ + $this->inTable( + array( + 'name' => 'tbody', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inTableBody($token); + + /* A start tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'table' + ) { + /* Parse error. Act as if an end tag token with the tag name "table" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inTable( + array( + 'name' => 'table', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->mainPhase($token); + + /* An end tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table' + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + return false; + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a table element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a table element has been + popped from the stack. */ + while (true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($current === 'table') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array( + 'body', + 'caption', + 'col', + 'colgroup', + 'html', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Parse error. Process the token as if the insertion mode was "in + body", with the following exception: */ + + /* If the current node is a table, tbody, tfoot, thead, or tr + element, then, whenever a node would be inserted into the current + node, it must instead be inserted into the foster parent element. */ + if (in_array( + end($this->stack)->nodeName, + array('table', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* The foster parent element is the parent element of the last + table element in the stack of open elements, if there is a + table element and it has such a parent element. If there is no + table element in the stack of open elements (innerHTML case), + then the foster parent element is the first element in the + stack of open elements (the html element). Otherwise, if there + is a table element in the stack of open elements, but the last + table element in the stack of open elements has no parent, or + its parent node is not an element, then the foster parent + element is the element before the last table element in the + stack of open elements. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === 'table') { + $table = $this->stack[$n]; + break; + } + } + + if (isset($table) && $table->parentNode !== null) { + $this->foster_parent = $table->parentNode; + + } elseif (!isset($table)) { + $this->foster_parent = $this->stack[0]; + + } elseif (isset($table) && ($table->parentNode === null || + $table->parentNode->nodeType !== XML_ELEMENT_NODE) + ) { + $this->foster_parent = $this->stack[$n - 1]; + } + } + + $this->inBody($token); + } + } + + private function inCaption($token) + { + /* An end tag whose tag name is "caption" */ + if ($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a caption element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a caption element has + been popped from the stack. */ + while (true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($node === 'caption') { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag + name is "table" */ + } elseif (($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + )) || ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table') + ) { + /* Parse error. Act as if an end tag with the tag name "caption" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inCaption( + array( + 'name' => 'caption', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inTable($token); + + /* An end tag whose tag name is one of: "body", "col", "colgroup", + "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array( + 'body', + 'col', + 'colgroup', + 'html', + 'tbody', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inColumnGroup($token) + { + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { + /* Insert a col element for the token. Immediately pop the current + node off the stack of open elements. */ + $this->insertElement($token); + array_pop($this->stack); + + /* An end tag whose tag name is "colgroup" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'colgroup' + ) { + /* If the current node is the root html element, then this is a + parse error, ignore the token. (innerHTML case) */ + if (end($this->stack)->nodeName === 'html') { + // Ignore + + /* Otherwise, pop the current node (which will be a colgroup + element) from the stack of open elements. Switch the insertion + mode to "in table". */ + } else { + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* An end tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Act as if an end tag with the tag name "colgroup" had been seen, + and then, if that token wasn't ignored, reprocess the current token. */ + $this->inColumnGroup( + array( + 'name' => 'colgroup', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inTable($token); + } + } + + private function inTableBody($token) + { + $clear = array('tbody', 'tfoot', 'thead', 'html'); + + /* A start tag whose tag name is "tr" */ + if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Insert a tr element for the token, then switch the insertion + mode to "in row". */ + $this->insertElement($token); + $this->mode = self::IN_ROW; + + /* A start tag whose tag name is one of: "th", "td" */ + } elseif ($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td') + ) { + /* Parse error. Act as if a start tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inTableBody( + array( + 'name' => 'tr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inRow($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead')) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node from the stack of open elements. Switch + the insertion mode to "in table". */ + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ + } elseif (($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead') + )) || + ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table') + ) { + /* If the stack of open elements does not have a tbody, thead, or + tfoot element in table scope, this is a parse error. Ignore the + token. (innerHTML case) */ + if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Act as if an end tag with the same tag name as the current + node ("tbody", "tfoot", or "thead") had been seen, then + reprocess the current token. */ + $this->inTableBody( + array( + 'name' => end($this->stack)->nodeName, + 'type' => HTML5::ENDTAG + ) + ); + + return $this->mainPhase($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') + ) + ) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inRow($token) + { + $clear = array('tr', 'html'); + + /* A start tag whose tag name is one of: "th", "td" */ + if ($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td') + ) { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in cell". */ + $this->insertElement($token); + $this->mode = self::IN_CELL; + + /* Insert a marker at the end of the list of active formatting + elements. */ + $this->a_formatting[] = self::MARKER; + + /* An end tag whose tag name is "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node (which will be a tr element) from the + stack of open elements. Switch the insertion mode to "in table + body". */ + array_pop($this->stack); + $this->mode = self::IN_TBODY; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* Act as if an end tag with the tag name "tr" had been seen, then, + if that token wasn't ignored, reprocess the current token. */ + $this->inRow( + array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inCell($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead')) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Otherwise, act as if an end tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inRow( + array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inCell($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') + ) + ) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inCell($token) + { + /* An end tag whose tag name is one of: "td", "th" */ + if ($token['type'] === HTML5::ENDTAG && + ($token['name'] === 'td' || $token['name'] === 'th') + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token, then this is a + parse error and the token must be ignored. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Generate implied end tags, except for elements with the same + tag name as the token. */ + $this->generateImpliedEndTags(array($token['name'])); + + /* Now, if the current node is not an element with the same tag + name as the token, then this is a parse error. */ + // k + + /* Pop elements from this stack until an element with the same + tag name as the token has been popped from the stack. */ + while (true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($node === $token['name']) { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in row". (The current node + will be a tr element at this point.) */ + $this->mode = self::IN_ROW; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if (!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if (!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html') + ) + ) { + /* Parse error. Ignore the token. */ + + /* An end tag whose tag name is one of: "table", "tbody", "tfoot", + "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('table', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token (which can only + happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), + then this is a parse error and the token must be ignored. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inSelect($token) + { + /* Handle the token as follows: */ + + /* A character token */ + if ($token['type'] === HTML5::CHARACTR) { + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token whose tag name is "option" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'option' + ) { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if (end($this->stack)->nodeName === 'option') { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* A start tag token whose tag name is "optgroup" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'optgroup' + ) { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if (end($this->stack)->nodeName === 'option') { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the current node is an optgroup element, act as if an end tag + with the tag name "optgroup" had been seen. */ + if (end($this->stack)->nodeName === 'optgroup') { + $this->inSelect( + array( + 'name' => 'optgroup', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* An end tag token whose tag name is "optgroup" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'optgroup' + ) { + /* First, if the current node is an option element, and the node + immediately before it in the stack of open elements is an optgroup + element, then act as if an end tag with the tag name "option" had + been seen. */ + $elements_in_stack = count($this->stack); + + if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' && + $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup' + ) { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the current node is an optgroup element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if ($this->stack[$elements_in_stack - 1] === 'optgroup') { + array_pop($this->stack); + } + + /* An end tag token whose tag name is "option" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'option' + ) { + /* If the current node is an option element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if (end($this->stack)->nodeName === 'option') { + array_pop($this->stack); + } + + /* An end tag whose tag name is "select" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'select' + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // w/e + + /* Otherwise: */ + } else { + /* Pop elements from the stack of open elements until a select + element has been popped from the stack. */ + while (true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($current === 'select') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* A start tag whose tag name is "select" */ + } elseif ($token['name'] === 'select' && + $token['type'] === HTML5::STARTTAG + ) { + /* Parse error. Act as if the token had been an end tag with the + tag name "select" instead. */ + $this->inSelect( + array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + ) + ); + + /* An end tag whose tag name is one of: "caption", "table", "tbody", + "tfoot", "thead", "tr", "td", "th" */ + } elseif (in_array( + $token['name'], + array( + 'caption', + 'table', + 'tbody', + 'tfoot', + 'thead', + 'tr', + 'td', + 'th' + ) + ) && $token['type'] === HTML5::ENDTAG + ) { + /* Parse error. */ + // w/e + + /* If the stack of open elements has an element in table scope with + the same tag name as that of the token, then act as if an end tag + with the tag name "select" had been seen, and reprocess the token. + Otherwise, ignore the token. */ + if ($this->elementInScope($token['name'], true)) { + $this->inSelect( + array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + ) + ); + + $this->mainPhase($token); + } + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterBody($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Process the token as it would be processed if the insertion mode + was "in body". */ + $this->inBody($token); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the first element in the stack of open + elements (the html element), with the data attribute set to the + data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->stack[0]->appendChild($comment); + + /* An end tag with the tag name "html" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { + /* If the parser was originally created in order to handle the + setting of an element's innerHTML attribute, this is a parse error; + ignore the token. (The element will be an html element in this + case.) (innerHTML case) */ + + /* Otherwise, switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* Anything else */ + } else { + /* Parse error. Set the insertion mode to "in body" and reprocess + the token. */ + $this->mode = self::IN_BODY; + return $this->inBody($token); + } + } + + private function inFrameset($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag with the tag name "frameset" */ + } elseif ($token['name'] === 'frameset' && + $token['type'] === HTML5::STARTTAG + ) { + $this->insertElement($token); + + /* An end tag with the tag name "frameset" */ + } elseif ($token['name'] === 'frameset' && + $token['type'] === HTML5::ENDTAG + ) { + /* If the current node is the root html element, then this is a + parse error; ignore the token. (innerHTML case) */ + if (end($this->stack)->nodeName === 'html') { + // Ignore + + } else { + /* Otherwise, pop the current node from the stack of open + elements. */ + array_pop($this->stack); + + /* If the parser was not originally created in order to handle + the setting of an element's innerHTML attribute (innerHTML case), + and the current node is no longer a frameset element, then change + the insertion mode to "after frameset". */ + $this->mode = self::AFTR_FRAME; + } + + /* A start tag with the tag name "frame" */ + } elseif ($token['name'] === 'frame' && + $token['type'] === HTML5::STARTTAG + ) { + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + + /* A start tag with the tag name "noframes" */ + } elseif ($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG + ) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterFrameset($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* An end tag with the tag name "html" */ + } elseif ($token['name'] === 'html' && + $token['type'] === HTML5::ENDTAG + ) { + /* Switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* A start tag with the tag name "noframes" */ + } elseif ($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG + ) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function trailingEndPhase($token) + { + /* After the main phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Process the token as it would be processed in the main phase. */ + $this->mainPhase($token); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or a start tag token. Or an end tag token. */ + } elseif (($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG + ) { + /* Parse error. Switch back to the main phase and reprocess the + token. */ + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + + /* An end-of-file token */ + } elseif ($token['type'] === HTML5::EOF) { + /* OMG DONE!! */ + } + } + + private function insertElement($token, $append = true, $check = false) + { + // Proprietary workaround for libxml2's limitations with tag names + if ($check) { + // Slightly modified HTML5 tag-name modification, + // removing anything that's not an ASCII letter, digit, or hyphen + $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); + // Remove leading hyphens and numbers + $token['name'] = ltrim($token['name'], '-0..9'); + // In theory, this should ever be needed, but just in case + if ($token['name'] === '') { + $token['name'] = 'span'; + } // arbitrary generic choice + } + + $el = $this->dom->createElement($token['name']); + + foreach ($token['attr'] as $attr) { + if (!$el->hasAttribute($attr['name'])) { + $el->setAttribute($attr['name'], $attr['value']); + } + } + + $this->appendToRealParent($el); + $this->stack[] = $el; + + return $el; + } + + private function insertText($data) + { + $text = $this->dom->createTextNode($data); + $this->appendToRealParent($text); + } + + private function insertComment($data) + { + $comment = $this->dom->createComment($data); + $this->appendToRealParent($comment); + } + + private function appendToRealParent($node) + { + if ($this->foster_parent === null) { + end($this->stack)->appendChild($node); + + } elseif ($this->foster_parent !== null) { + /* If the foster parent element is the parent element of the + last table element in the stack of open elements, then the new + node must be inserted immediately before the last table element + in the stack of open elements in the foster parent element; + otherwise, the new node must be appended to the foster parent + element. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === 'table' && + $this->stack[$n]->parentNode !== null + ) { + $table = $this->stack[$n]; + break; + } + } + + if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) { + $this->foster_parent->insertBefore($node, $table); + } else { + $this->foster_parent->appendChild($node); + } + + $this->foster_parent = null; + } + } + + private function elementInScope($el, $table = false) + { + if (is_array($el)) { + foreach ($el as $element) { + if ($this->elementInScope($element, $table)) { + return true; + } + } + + return false; + } + + $leng = count($this->stack); + + for ($n = 0; $n < $leng; $n++) { + /* 1. Initialise node to be the current node (the bottommost node of + the stack). */ + $node = $this->stack[$leng - 1 - $n]; + + if ($node->tagName === $el) { + /* 2. If node is the target node, terminate in a match state. */ + return true; + + } elseif ($node->tagName === 'table') { + /* 3. Otherwise, if node is a table element, terminate in a failure + state. */ + return false; + + } elseif ($table === true && in_array( + $node->tagName, + array( + 'caption', + 'td', + 'th', + 'button', + 'marquee', + 'object' + ) + ) + ) { + /* 4. Otherwise, if the algorithm is the "has an element in scope" + variant (rather than the "has an element in table scope" variant), + and node is one of the following, terminate in a failure state. */ + return false; + + } elseif ($node === $node->ownerDocument->documentElement) { + /* 5. Otherwise, if node is an html element (root element), terminate + in a failure state. (This can only happen if the node is the topmost + node of the stack of open elements, and prevents the next step from + being invoked if there are no more elements in the stack.) */ + return false; + } + + /* Otherwise, set node to the previous entry in the stack of open + elements and return to step 2. (This will never fail, since the loop + will always terminate in the previous step if the top of the stack + is reached.) */ + } + } + + private function reconstructActiveFormattingElements() + { + /* 1. If there are no entries in the list of active formatting elements, + then there is nothing to reconstruct; stop this algorithm. */ + $formatting_elements = count($this->a_formatting); + + if ($formatting_elements === 0) { + return false; + } + + /* 3. Let entry be the last (most recently added) element in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. If the last (most recently added) entry in the list of active + formatting elements is a marker, or if it is an element that is in the + stack of open elements, then there is nothing to reconstruct; stop this + algorithm. */ + if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { + return false; + } + + for ($a = $formatting_elements - 1; $a >= 0; true) { + /* 4. If there are no entries before entry in the list of active + formatting elements, then jump to step 8. */ + if ($a === 0) { + $step_seven = false; + break; + } + + /* 5. Let entry be the entry one earlier than entry in the list of + active formatting elements. */ + $a--; + $entry = $this->a_formatting[$a]; + + /* 6. If entry is neither a marker nor an element that is also in + thetack of open elements, go to step 4. */ + if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { + break; + } + } + + while (true) { + /* 7. Let entry be the element one later than entry in the list of + active formatting elements. */ + if (isset($step_seven) && $step_seven === true) { + $a++; + $entry = $this->a_formatting[$a]; + } + + /* 8. Perform a shallow clone of the element entry to obtain clone. */ + $clone = $entry->cloneNode(); + + /* 9. Append clone to the current node and push it onto the stack + of open elements so that it is the new current node. */ + end($this->stack)->appendChild($clone); + $this->stack[] = $clone; + + /* 10. Replace the entry for entry in the list with an entry for + clone. */ + $this->a_formatting[$a] = $clone; + + /* 11. If the entry for clone in the list of active formatting + elements is not the last entry in the list, return to step 7. */ + if (end($this->a_formatting) !== $clone) { + $step_seven = true; + } else { + break; + } + } + } + + private function clearTheActiveFormattingElementsUpToTheLastMarker() + { + /* When the steps below require the UA to clear the list of active + formatting elements up to the last marker, the UA must perform the + following steps: */ + + while (true) { + /* 1. Let entry be the last (most recently added) entry in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. Remove entry from the list of active formatting elements. */ + array_pop($this->a_formatting); + + /* 3. If entry was a marker, then stop the algorithm at this point. + The list has been cleared up to the last marker. */ + if ($entry === self::MARKER) { + break; + } + } + } + + private function generateImpliedEndTags($exclude = array()) + { + /* When the steps below require the UA to generate implied end tags, + then, if the current node is a dd element, a dt element, an li element, + a p element, a td element, a th element, or a tr element, the UA must + act as if an end tag with the respective tag name had been seen and + then generate implied end tags again. */ + $node = end($this->stack); + $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); + + while (in_array(end($this->stack)->nodeName, $elements)) { + array_pop($this->stack); + } + } + + private function getElementCategory($node) + { + $name = $node->tagName; + if (in_array($name, $this->special)) { + return self::SPECIAL; + } elseif (in_array($name, $this->scoping)) { + return self::SCOPING; + } elseif (in_array($name, $this->formatting)) { + return self::FORMATTING; + } else { + return self::PHRASING; + } + } + + private function clearStackToTableContext($elements) + { + /* When the steps above require the UA to clear the stack back to a + table context, it means that the UA must, while the current node is not + a table element or an html element, pop elements from the stack of open + elements. If this causes any elements to be popped from the stack, then + this is a parse error. */ + while (true) { + $node = end($this->stack)->nodeName; + + if (in_array($node, $elements)) { + break; + } else { + array_pop($this->stack); + } + } + } + + private function resetInsertionMode() + { + /* 1. Let last be false. */ + $last = false; + $leng = count($this->stack); + + for ($n = $leng - 1; $n >= 0; $n--) { + /* 2. Let node be the last node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 3. If node is the first node in the stack of open elements, then + set last to true. If the element whose innerHTML attribute is being + set is neither a td element nor a th element, then set node to the + element whose innerHTML attribute is being set. (innerHTML case) */ + if ($this->stack[0]->isSameNode($node)) { + $last = true; + } + + /* 4. If node is a select element, then switch the insertion mode to + "in select" and abort these steps. (innerHTML case) */ + if ($node->nodeName === 'select') { + $this->mode = self::IN_SELECT; + break; + + /* 5. If node is a td or th element, then switch the insertion mode + to "in cell" and abort these steps. */ + } elseif ($node->nodeName === 'td' || $node->nodeName === 'th') { + $this->mode = self::IN_CELL; + break; + + /* 6. If node is a tr element, then switch the insertion mode to + "in row" and abort these steps. */ + } elseif ($node->nodeName === 'tr') { + $this->mode = self::IN_ROW; + break; + + /* 7. If node is a tbody, thead, or tfoot element, then switch the + insertion mode to "in table body" and abort these steps. */ + } elseif (in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { + $this->mode = self::IN_TBODY; + break; + + /* 8. If node is a caption element, then switch the insertion mode + to "in caption" and abort these steps. */ + } elseif ($node->nodeName === 'caption') { + $this->mode = self::IN_CAPTION; + break; + + /* 9. If node is a colgroup element, then switch the insertion mode + to "in column group" and abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'colgroup') { + $this->mode = self::IN_CGROUP; + break; + + /* 10. If node is a table element, then switch the insertion mode + to "in table" and abort these steps. */ + } elseif ($node->nodeName === 'table') { + $this->mode = self::IN_TABLE; + break; + + /* 11. If node is a head element, then switch the insertion mode + to "in body" ("in body"! not "in head"!) and abort these steps. + (innerHTML case) */ + } elseif ($node->nodeName === 'head') { + $this->mode = self::IN_BODY; + break; + + /* 12. If node is a body element, then switch the insertion mode to + "in body" and abort these steps. */ + } elseif ($node->nodeName === 'body') { + $this->mode = self::IN_BODY; + break; + + /* 13. If node is a frameset element, then switch the insertion + mode to "in frameset" and abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'frameset') { + $this->mode = self::IN_FRAME; + break; + + /* 14. If node is an html element, then: if the head element + pointer is null, switch the insertion mode to "before head", + otherwise, switch the insertion mode to "after head". In either + case, abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'html') { + $this->mode = ($this->head_pointer === null) + ? self::BEFOR_HEAD + : self::AFTER_HEAD; + + break; + + /* 15. If last is true, then set the insertion mode to "in body" + and abort these steps. (innerHTML case) */ + } elseif ($last) { + $this->mode = self::IN_BODY; + break; + } + } + } + + private function closeCell() + { + /* If the stack of open elements has a td or th element in table scope, + then act as if an end tag token with that tag name had been seen. */ + foreach (array('td', 'th') as $cell) { + if ($this->elementInScope($cell, true)) { + $this->inCell( + array( + 'name' => $cell, + 'type' => HTML5::ENDTAG + ) + ); + + break; + } + } + } + + public function save() + { + return $this->dom; + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php new file mode 100644 index 00000000000..3995fec9fe6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php @@ -0,0 +1,49 @@ +data = $data; + $this->line = $line; + $this->col = $col; + } + + public function toTokenPair() { + return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null); + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php new file mode 100644 index 00000000000..6cbf56dadac --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php @@ -0,0 +1,59 @@ + form or the form, i.e. + * is it a pair of start/end tokens or an empty token. + * @bool + */ + public $empty = false; + + public $endCol = null, $endLine = null, $endArmor = array(); + + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { + $this->name = $name; + $this->attr = $attr; + $this->line = $line; + $this->col = $col; + $this->armor = $armor; + } + + public function toTokenPair() { + // XXX inefficiency here, normalization is not necessary + if ($this->empty) { + return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null); + } else { + $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor); + $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor); + //$end->start = $start; + return array($start, $end); + } + } +} + diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php new file mode 100644 index 00000000000..aec91664730 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php @@ -0,0 +1,54 @@ +data = $data; + $this->is_whitespace = $is_whitespace; + $this->line = $line; + $this->col = $col; + } + + public function toTokenPair() { + return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php new file mode 100644 index 00000000000..18c8bbb00ad --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php @@ -0,0 +1,111 @@ +preserve[$i] = true; + } + for ($i = 65; $i <= 90; $i++) { // upper-case + $this->preserve[$i] = true; + } + for ($i = 97; $i <= 122; $i++) { // lower-case + $this->preserve[$i] = true; + } + $this->preserve[45] = true; // Dash - + $this->preserve[46] = true; // Period . + $this->preserve[95] = true; // Underscore _ + $this->preserve[126]= true; // Tilde ~ + + // extra letters not to escape + if ($preserve !== false) { + for ($i = 0, $c = strlen($preserve); $i < $c; $i++) { + $this->preserve[ord($preserve[$i])] = true; + } + } + } + + /** + * Our replacement for urlencode, it encodes all non-reserved characters, + * as well as any extra characters that were instructed to be preserved. + * @note + * Assumes that the string has already been normalized, making any + * and all percent escape sequences valid. Percents will not be + * re-escaped, regardless of their status in $preserve + * @param string $string String to be encoded + * @return string Encoded string. + */ + public function encode($string) + { + $ret = ''; + for ($i = 0, $c = strlen($string); $i < $c; $i++) { + if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) { + $ret .= '%' . sprintf('%02X', $int); + } else { + $ret .= $string[$i]; + } + } + return $ret; + } + + /** + * Fix up percent-encoding by decoding unreserved characters and normalizing. + * @warning This function is affected by $preserve, even though the + * usual desired behavior is for this not to preserve those + * characters. Be careful when reusing instances of PercentEncoder! + * @param string $string String to normalize + * @return string + */ + public function normalize($string) + { + if ($string == '') { + return ''; + } + $parts = explode('%', $string); + $ret = array_shift($parts); + foreach ($parts as $part) { + $length = strlen($part); + if ($length < 2) { + $ret .= '%25' . $part; + continue; + } + $encoding = substr($part, 0, 2); + $text = substr($part, 2); + if (!ctype_xdigit($encoding)) { + $ret .= '%25' . $part; + continue; + } + $int = hexdec($encoding); + if (isset($this->preserve[$int])) { + $ret .= chr($int) . $text; + continue; + } + $encoding = strtoupper($encoding); + $ret .= '%' . $encoding . $text; + } + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php new file mode 100644 index 00000000000..549e4cea1ec --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php @@ -0,0 +1,218 @@ +getAll(); + $context = new HTMLPurifier_Context(); + $this->generator = new HTMLPurifier_Generator($config, $context); + } + + /** + * Main function that renders object or aspect of that object + * @note Parameters vary depending on printer + */ + // function render() {} + + /** + * Returns a start tag + * @param string $tag Tag name + * @param array $attr Attribute array + * @return string + */ + protected function start($tag, $attr = array()) + { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) + ); + } + + /** + * Returns an end tag + * @param string $tag Tag name + * @return string + */ + protected function end($tag) + { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_End($tag) + ); + } + + /** + * Prints a complete element with content inside + * @param string $tag Tag name + * @param string $contents Element contents + * @param array $attr Tag attributes + * @param bool $escape whether or not to escape contents + * @return string + */ + protected function element($tag, $contents, $attr = array(), $escape = true) + { + return $this->start($tag, $attr) . + ($escape ? $this->escape($contents) : $contents) . + $this->end($tag); + } + + /** + * @param string $tag + * @param array $attr + * @return string + */ + protected function elementEmpty($tag, $attr = array()) + { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Empty($tag, $attr) + ); + } + + /** + * @param string $text + * @return string + */ + protected function text($text) + { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Text($text) + ); + } + + /** + * Prints a simple key/value row in a table. + * @param string $name Key + * @param mixed $value Value + * @return string + */ + protected function row($name, $value) + { + if (is_bool($value)) { + $value = $value ? 'On' : 'Off'; + } + return + $this->start('tr') . "\n" . + $this->element('th', $name) . "\n" . + $this->element('td', $value) . "\n" . + $this->end('tr'); + } + + /** + * Escapes a string for HTML output. + * @param string $string String to escape + * @return string + */ + protected function escape($string) + { + $string = HTMLPurifier_Encoder::cleanUTF8($string); + $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); + return $string; + } + + /** + * Takes a list of strings and turns them into a single list + * @param string[] $array List of strings + * @param bool $polite Bool whether or not to add an end before the last + * @return string + */ + protected function listify($array, $polite = false) + { + if (empty($array)) { + return 'None'; + } + $ret = ''; + $i = count($array); + foreach ($array as $value) { + $i--; + $ret .= $value; + if ($i > 0 && !($polite && $i == 1)) { + $ret .= ', '; + } + if ($polite && $i == 1) { + $ret .= 'and '; + } + } + return $ret; + } + + /** + * Retrieves the class of an object without prefixes, as well as metadata + * @param object $obj Object to determine class of + * @param string $sec_prefix Further prefix to remove + * @return string + */ + protected function getClass($obj, $sec_prefix = '') + { + static $five = null; + if ($five === null) { + $five = version_compare(PHP_VERSION, '5', '>='); + } + $prefix = 'HTMLPurifier_' . $sec_prefix; + if (!$five) { + $prefix = strtolower($prefix); + } + $class = str_replace($prefix, '', get_class($obj)); + $lclass = strtolower($class); + $class .= '('; + switch ($lclass) { + case 'enum': + $values = array(); + foreach ($obj->valid_values as $value => $bool) { + $values[] = $value; + } + $class .= implode(', ', $values); + break; + case 'css_composite': + $values = array(); + foreach ($obj->defs as $def) { + $values[] = $this->getClass($def, $sec_prefix); + } + $class .= implode(', ', $values); + break; + case 'css_multiple': + $class .= $this->getClass($obj->single, $sec_prefix) . ', '; + $class .= $obj->max; + break; + case 'css_denyelementdecorator': + $class .= $this->getClass($obj->def, $sec_prefix) . ', '; + $class .= $obj->element; + break; + case 'css_importantdecorator': + $class .= $this->getClass($obj->def, $sec_prefix); + if ($obj->allow) { + $class .= ', !important'; + } + break; + } + $class .= ')'; + return $class; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php new file mode 100644 index 00000000000..29505fe12d4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php @@ -0,0 +1,44 @@ +def = $config->getCSSDefinition(); + $ret = ''; + + $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); + $ret .= $this->start('table'); + + $ret .= $this->element('caption', 'Properties ($info)'); + + $ret .= $this->start('thead'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Property', array('class' => 'heavy')); + $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;')); + $ret .= $this->end('tr'); + $ret .= $this->end('thead'); + + ksort($this->def->info); + foreach ($this->def->info as $property => $obj) { + $name = $this->getClass($obj, 'AttrDef_'); + $ret .= $this->row($property, $name); + } + + $ret .= $this->end('table'); + $ret .= $this->end('div'); + + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css new file mode 100644 index 00000000000..3ff1a88aa42 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css @@ -0,0 +1,10 @@ + +.hp-config {} + +.hp-config tbody th {text-align:right; padding-right:0.5em;} +.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;} +.hp-config .namespace th {text-align:center;} +.hp-config .verbose {display:none;} +.hp-config .controls {text-align:center;} + +/* vim: et sw=4 sts=4 */ diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js new file mode 100644 index 00000000000..cba00c9b80b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js @@ -0,0 +1,5 @@ +function toggleWriteability(id_of_patient, checked) { + document.getElementById(id_of_patient).disabled = checked; +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php new file mode 100644 index 00000000000..33ae11397e5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php @@ -0,0 +1,451 @@ +docURL = $doc_url; + $this->name = $name; + $this->compress = $compress; + // initialize sub-printers + $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); + $this->fields[HTMLPurifier_VarParser::C_BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); + } + + /** + * Sets default column and row size for textareas in sub-printers + * @param $cols Integer columns of textarea, null to use default + * @param $rows Integer rows of textarea, null to use default + */ + public function setTextareaDimensions($cols = null, $rows = null) + { + if ($cols) { + $this->fields['default']->cols = $cols; + } + if ($rows) { + $this->fields['default']->rows = $rows; + } + } + + /** + * Retrieves styling, in case it is not accessible by webserver + */ + public static function getCSS() + { + return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); + } + + /** + * Retrieves JavaScript, in case it is not accessible by webserver + */ + public static function getJavaScript() + { + return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); + } + + /** + * Returns HTML output for a configuration form + * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array + * where [0] has an HTML namespace and [1] is being rendered. + * @param array|bool $allowed Optional namespace(s) and directives to restrict form to. + * @param bool $render_controls + * @return string + */ + public function render($config, $allowed = true, $render_controls = true) + { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + + $this->config = $config; + $this->genConfig = $gen_config; + $this->prepareGenerator($gen_config); + + $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def); + $all = array(); + foreach ($allowed as $key) { + list($ns, $directive) = $key; + $all[$ns][$directive] = $config->get($ns . '.' . $directive); + } + + $ret = ''; + $ret .= $this->start('table', array('class' => 'hp-config')); + $ret .= $this->start('thead'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); + $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); + $ret .= $this->end('tr'); + $ret .= $this->end('thead'); + foreach ($all as $ns => $directives) { + $ret .= $this->renderNamespace($ns, $directives); + } + if ($render_controls) { + $ret .= $this->start('tbody'); + $ret .= $this->start('tr'); + $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); + $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); + $ret .= '[Reset]'; + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders a single namespace + * @param $ns String namespace name + * @param array $directives array of directives to values + * @return string + */ + protected function renderNamespace($ns, $directives) + { + $ret = ''; + $ret .= $this->start('tbody', array('class' => 'namespace')); + $ret .= $this->start('tr'); + $ret .= $this->element('th', $ns, array('colspan' => 2)); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); + $ret .= $this->start('tbody'); + foreach ($directives as $directive => $value) { + $ret .= $this->start('tr'); + $ret .= $this->start('th'); + if ($this->docURL) { + $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); + $ret .= $this->start('a', array('href' => $url)); + } + $attr = array('for' => "{$this->name}:$ns.$directive"); + + // crop directive name if it's too long + if (!$this->compress || (strlen($directive) < $this->compress)) { + $directive_disp = $directive; + } else { + $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; + $attr['title'] = $directive; + } + + $ret .= $this->element( + 'label', + $directive_disp, + // component printers must create an element with this id + $attr + ); + if ($this->docURL) { + $ret .= $this->end('a'); + } + $ret .= $this->end('th'); + + $ret .= $this->start('td'); + $def = $this->config->def->info["$ns.$directive"]; + if (is_int($def)) { + $allow_null = $def < 0; + $type = abs($def); + } else { + $type = $def->type; + $allow_null = isset($def->allow_null); + } + if (!isset($this->fields[$type])) { + $type = 0; + } // default + $type_obj = $this->fields[$type]; + if ($allow_null) { + $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); + } + $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + } + $ret .= $this->end('tbody'); + return $ret; + } + +} + +/** + * Printer decorator for directives that accept null + */ +class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer +{ + /** + * Printer being decorated + * @type HTMLPurifier_Printer + */ + protected $obj; + + /** + * @param HTMLPurifier_Printer $obj Printer to decorate + */ + public function __construct($obj) + { + parent::__construct(); + $this->obj = $obj; + } + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + + $ret = ''; + $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Null/Disabled'); + $ret .= $this->end('label'); + $attr = array( + 'type' => 'checkbox', + 'value' => '1', + 'class' => 'null-toggle', + 'name' => "$name" . "[Null_$ns.$directive]", + 'id' => "$name:Null_$ns.$directive", + 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! + ); + if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { + // modify inline javascript slightly + $attr['onclick'] = + "toggleWriteability('$name:Yes_$ns.$directive',checked);" . + "toggleWriteability('$name:No_$ns.$directive',checked)"; + } + if ($value === null) { + $attr['checked'] = 'checked'; + } + $ret .= $this->elementEmpty('input', $attr); + $ret .= $this->text(' or '); + $ret .= $this->elementEmpty('br'); + $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config)); + return $ret; + } +} + +/** + * Swiss-army knife configuration form field printer + */ +class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer +{ + /** + * @type int + */ + public $cols = 18; + + /** + * @type int + */ + public $rows = 5; + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + // this should probably be split up a little + $ret = ''; + $def = $config->def->info["$ns.$directive"]; + if (is_int($def)) { + $type = abs($def); + } else { + $type = $def->type; + } + if (is_array($value)) { + switch ($type) { + case HTMLPurifier_VarParser::LOOKUP: + $array = $value; + $value = array(); + foreach ($array as $val => $b) { + $value[] = $val; + } + //TODO does this need a break? + case HTMLPurifier_VarParser::ALIST: + $value = implode(PHP_EOL, $value); + break; + case HTMLPurifier_VarParser::HASH: + $nvalue = ''; + foreach ($value as $i => $v) { + if (is_array($v)) { + // HACK + $v = implode(";", $v); + } + $nvalue .= "$i:$v" . PHP_EOL; + } + $value = $nvalue; + break; + default: + $value = ''; + } + } + if ($type === HTMLPurifier_VarParser::C_MIXED) { + return 'Not supported'; + $value = serialize($value); + } + $attr = array( + 'name' => "$name" . "[$ns.$directive]", + 'id' => "$name:$ns.$directive" + ); + if ($value === null) { + $attr['disabled'] = 'disabled'; + } + if (isset($def->allowed)) { + $ret .= $this->start('select', $attr); + foreach ($def->allowed as $val => $b) { + $attr = array(); + if ($value == $val) { + $attr['selected'] = 'selected'; + } + $ret .= $this->element('option', $val, $attr); + } + $ret .= $this->end('select'); + } elseif ($type === HTMLPurifier_VarParser::TEXT || + $type === HTMLPurifier_VarParser::ITEXT || + $type === HTMLPurifier_VarParser::ALIST || + $type === HTMLPurifier_VarParser::HASH || + $type === HTMLPurifier_VarParser::LOOKUP) { + $attr['cols'] = $this->cols; + $attr['rows'] = $this->rows; + $ret .= $this->start('textarea', $attr); + $ret .= $this->text($value); + $ret .= $this->end('textarea'); + } else { + $attr['value'] = $value; + $attr['type'] = 'text'; + $ret .= $this->elementEmpty('input', $attr); + } + return $ret; + } +} + +/** + * Bool form field printer + */ +class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer +{ + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + $ret = ''; + $ret .= $this->start('div', array('id' => "$name:$ns.$directive")); + + $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Yes'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "$name" . "[$ns.$directive]", + 'id' => "$name:Yes_$ns.$directive", + 'value' => '1' + ); + if ($value === true) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' No'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "$name" . "[$ns.$directive]", + 'id' => "$name:No_$ns.$directive", + 'value' => '0' + ); + if ($value === false) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->end('div'); + + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php new file mode 100644 index 00000000000..ae8639176e0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php @@ -0,0 +1,324 @@ +config =& $config; + + $this->def = $config->getHTMLDefinition(); + + $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); + + $ret .= $this->renderDoctype(); + $ret .= $this->renderEnvironment(); + $ret .= $this->renderContentSets(); + $ret .= $this->renderInfo(); + + $ret .= $this->end('div'); + + return $ret; + } + + /** + * Renders the Doctype table + * @return string + */ + protected function renderDoctype() + { + $doctype = $this->def->doctype; + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Doctype'); + $ret .= $this->row('Name', $doctype->name); + $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No'); + $ret .= $this->row('Default Modules', implode(', ', $doctype->modules)); + $ret .= $this->row('Default Tidy Modules', implode(', ', $doctype->tidyModules)); + $ret .= $this->end('table'); + return $ret; + } + + + /** + * Renders environment table, which is miscellaneous info + * @return string + */ + protected function renderEnvironment() + { + $def = $this->def; + + $ret = ''; + + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Environment'); + + $ret .= $this->row('Parent of fragment', $def->info_parent); + $ret .= $this->renderChildren($def->info_parent_def->child); + $ret .= $this->row('Block wrap name', $def->info_block_wrapper); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Global attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Tag transforms'); + $list = array(); + foreach ($def->info_tag_transform as $old => $new) { + $new = $this->getClass($new, 'TagTransform_'); + $list[] = "<$old> with $new"; + } + $ret .= $this->element('td', $this->listify($list)); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); + $ret .= $this->end('tr'); + + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders the Content Sets table + * @return string + */ + protected function renderContentSets() + { + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Content Sets'); + foreach ($this->def->info_content_sets as $name => $lookup) { + $ret .= $this->heavyHeader($name); + $ret .= $this->start('tr'); + $ret .= $this->element('td', $this->listifyTagLookup($lookup)); + $ret .= $this->end('tr'); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders the Elements ($info) table + * @return string + */ + protected function renderInfo() + { + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Elements ($info)'); + ksort($this->def->info); + $ret .= $this->heavyHeader('Allowed tags', 2); + $ret .= $this->start('tr'); + $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2)); + $ret .= $this->end('tr'); + foreach ($this->def->info as $name => $def) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2)); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Inline content'); + $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); + $ret .= $this->end('tr'); + if (!empty($def->excludes)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Excludes'); + $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); + $ret .= $this->end('tr'); + } + if (!empty($def->attr_transform_pre)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); + $ret .= $this->end('tr'); + } + if (!empty($def->attr_transform_post)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); + $ret .= $this->end('tr'); + } + if (!empty($def->auto_close)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Auto closed by'); + $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); + $ret .= $this->end('tr'); + } + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Allowed attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0); + $ret .= $this->end('tr'); + + if (!empty($def->required_attr)) { + $ret .= $this->row('Required attributes', $this->listify($def->required_attr)); + } + + $ret .= $this->renderChildren($def->child); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders a row describing the allowed children of an element + * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element + * @return string + */ + protected function renderChildren($def) + { + $context = new HTMLPurifier_Context(); + $ret = ''; + $ret .= $this->start('tr'); + $elements = array(); + $attr = array(); + if (isset($def->elements)) { + if ($def->type == 'strictblockquote') { + $def->validateChildren(array(), $this->config, $context); + } + $elements = $def->elements; + } + if ($def->type == 'chameleon') { + $attr['rowspan'] = 2; + } elseif ($def->type == 'empty') { + $elements = array(); + } elseif ($def->type == 'table') { + $elements = array_flip( + array( + 'col', + 'caption', + 'colgroup', + 'thead', + 'tfoot', + 'tbody', + 'tr' + ) + ); + } + $ret .= $this->element('th', 'Allowed children', $attr); + + if ($def->type == 'chameleon') { + + $ret .= $this->element( + 'td', + 'Block: ' . + $this->escape($this->listifyTagLookup($def->block->elements)), + null, + 0 + ); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element( + 'td', + 'Inline: ' . + $this->escape($this->listifyTagLookup($def->inline->elements)), + null, + 0 + ); + + } elseif ($def->type == 'custom') { + + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $def->dtd_regex + ); + + } else { + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $this->escape($this->listifyTagLookup($elements)), + null, + 0 + ); + } + $ret .= $this->end('tr'); + return $ret; + } + + /** + * Listifies a tag lookup table. + * @param array $array Tag lookup array in form of array('tagname' => true) + * @return string + */ + protected function listifyTagLookup($array) + { + ksort($array); + $list = array(); + foreach ($array as $name => $discard) { + if ($name !== '#PCDATA' && !isset($this->def->info[$name])) { + continue; + } + $list[] = $name; + } + return $this->listify($list); + } + + /** + * Listifies a list of objects by retrieving class names and internal state + * @param array $array List of objects + * @return string + * @todo Also add information about internal state + */ + protected function listifyObjectList($array) + { + ksort($array); + $list = array(); + foreach ($array as $obj) { + $list[] = $this->getClass($obj, 'AttrTransform_'); + } + return $this->listify($list); + } + + /** + * Listifies a hash of attributes to AttrDef classes + * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + * @return string + */ + protected function listifyAttr($array) + { + ksort($array); + $list = array(); + foreach ($array as $name => $obj) { + if ($obj === false) { + continue; + } + $list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . ''; + } + return $this->listify($list); + } + + /** + * Creates a heavy header row + * @param string $text + * @param int $num + * @return string + */ + protected function heavyHeader($text, $num = 1) + { + $ret = ''; + $ret .= $this->start('tr'); + $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); + $ret .= $this->end('tr'); + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php new file mode 100644 index 00000000000..189348fd9ec --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php @@ -0,0 +1,122 @@ +parent = $parent; + } + + /** + * Recursively retrieves the value for a key + * @param string $name + * @throws HTMLPurifier_Exception + */ + public function get($name) + { + if ($this->has($name)) { + return $this->data[$name]; + } + // possible performance bottleneck, convert to iterative if necessary + if ($this->parent) { + return $this->parent->get($name); + } + throw new HTMLPurifier_Exception("Key '$name' not found"); + } + + /** + * Sets the value of a key, for this plist + * @param string $name + * @param mixed $value + */ + public function set($name, $value) + { + $this->data[$name] = $value; + } + + /** + * Returns true if a given key exists + * @param string $name + * @return bool + */ + public function has($name) + { + return array_key_exists($name, $this->data); + } + + /** + * Resets a value to the value of it's parent, usually the default. If + * no value is specified, the entire plist is reset. + * @param string $name + */ + public function reset($name = null) + { + if ($name == null) { + $this->data = array(); + } else { + unset($this->data[$name]); + } + } + + /** + * Squashes this property list and all of its property lists into a single + * array, and returns the array. This value is cached by default. + * @param bool $force If true, ignores the cache and regenerates the array. + * @return array + */ + public function squash($force = false) + { + if ($this->cache !== null && !$force) { + return $this->cache; + } + if ($this->parent) { + return $this->cache = array_merge($this->parent->squash($force), $this->data); + } else { + return $this->cache = $this->data; + } + } + + /** + * Returns the parent plist. + * @return HTMLPurifier_PropertyList + */ + public function getParent() + { + return $this->parent; + } + + /** + * Sets the parent plist. + * @param HTMLPurifier_PropertyList $plist Parent plist + */ + public function setParent($plist) + { + $this->parent = $plist; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php new file mode 100644 index 00000000000..15b330ea308 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php @@ -0,0 +1,42 @@ +l = strlen($filter); + $this->filter = $filter; + } + + /** + * @return bool + */ + public function accept() + { + $key = $this->getInnerIterator()->key(); + if (strncmp($key, $this->filter, $this->l) !== 0) { + return false; + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php new file mode 100644 index 00000000000..f58db9042ae --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php @@ -0,0 +1,56 @@ +input = $input; + $this->output = array(); + } + + /** + * Shifts an element off the front of the queue. + */ + public function shift() { + if (empty($this->output)) { + $this->output = array_reverse($this->input); + $this->input = array(); + } + if (empty($this->output)) { + return NULL; + } + return array_pop($this->output); + } + + /** + * Pushes an element onto the front of the queue. + */ + public function push($x) { + array_push($this->input, $x); + } + + /** + * Checks if it's empty. + */ + public function isEmpty() { + return empty($this->input) && empty($this->output); + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php new file mode 100644 index 00000000000..e1ff3b72df7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php @@ -0,0 +1,26 @@ +strategies as $strategy) { + $tokens = $strategy->execute($tokens, $config, $context); + } + return $tokens; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php new file mode 100644 index 00000000000..4414c17d6e7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php @@ -0,0 +1,17 @@ +strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); + $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); + $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); + $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php new file mode 100644 index 00000000000..6fa673db9ae --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php @@ -0,0 +1,181 @@ +getHTMLDefinition(); + + $excludes_enabled = !$config->get('Core.DisableExcludes'); + + // setup the context variable 'IsInline', for chameleon processing + // is 'false' when we are not inline, 'true' when it must always + // be inline, and an integer when it is inline for a certain + // branch of the document tree + $is_inline = $definition->info_parent_def->descendants_are_inline; + $context->register('IsInline', $is_inline); + + // setup error collector + $e =& $context->get('ErrorCollector', true); + + //####################################################################// + // Loop initialization + + // stack that contains all elements that are excluded + // it is organized by parent elements, similar to $stack, + // but it is only populated when an element with exclusions is + // processed, i.e. there won't be empty exclusions. + $exclude_stack = array($definition->info_parent_def->excludes); + + // variable that contains the start token while we are processing + // nodes. This enables error reporting to do its job + $node = $top_node; + // dummy token + list($token, $d) = $node->toTokenPair(); + $context->register('CurrentNode', $node); + $context->register('CurrentToken', $token); + + //####################################################################// + // Loop + + // We need to implement a post-order traversal iteratively, to + // avoid running into stack space limits. This is pretty tricky + // to reason about, so we just manually stack-ify the recursive + // variant: + // + // function f($node) { + // foreach ($node->children as $child) { + // f($child); + // } + // validate($node); + // } + // + // Thus, we will represent a stack frame as array($node, + // $is_inline, stack of children) + // e.g. array_reverse($node->children) - already processed + // children. + + $parent_def = $definition->info_parent_def; + $stack = array( + array($top_node, + $parent_def->descendants_are_inline, + $parent_def->excludes, // exclusions + 0) + ); + + while (!empty($stack)) { + list($node, $is_inline, $excludes, $ix) = array_pop($stack); + // recursive call + $go = false; + $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name]; + while (isset($node->children[$ix])) { + $child = $node->children[$ix++]; + if ($child instanceof HTMLPurifier_Node_Element) { + $go = true; + $stack[] = array($node, $is_inline, $excludes, $ix); + $stack[] = array($child, + // ToDo: I don't think it matters if it's def or + // child_def, but double check this... + $is_inline || $def->descendants_are_inline, + empty($def->excludes) ? $excludes + : array_merge($excludes, $def->excludes), + 0); + break; + } + }; + if ($go) continue; + list($token, $d) = $node->toTokenPair(); + // base case + if ($excludes_enabled && isset($excludes[$node->name])) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); + } else { + // XXX I suppose it would be slightly more efficient to + // avoid the allocation here and have children + // strategies handle it + $children = array(); + foreach ($node->children as $child) { + if (!$child->dead) $children[] = $child; + } + $result = $def->child->validateChildren($children, $config, $context); + if ($result === true) { + // nop + $node->children = $children; + } elseif ($result === false) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); + } else { + $node->children = $result; + if ($e) { + // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators + if (empty($result) && !empty($children)) { + $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); + } else if ($result != $children) { + $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); + } + } + } + } + } + + //####################################################################// + // Post-processing + + // remove context variables + $context->destroy('IsInline'); + $context->destroy('CurrentNode'); + $context->destroy('CurrentToken'); + + //####################################################################// + // Return + + return HTMLPurifier_Arborize::flatten($node, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php new file mode 100644 index 00000000000..a6eb09e453e --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php @@ -0,0 +1,659 @@ +getHTMLDefinition(); + + // local variables + $generator = new HTMLPurifier_Generator($config, $context); + $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); + // used for autoclose early abortion + $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config); + $e = $context->get('ErrorCollector', true); + $i = false; // injector index + list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens); + if ($token === NULL) { + return array(); + } + $reprocess = false; // whether or not to reprocess the same token + $stack = array(); + + // member variables + $this->stack =& $stack; + $this->tokens =& $tokens; + $this->token =& $token; + $this->zipper =& $zipper; + $this->config = $config; + $this->context = $context; + + // context variables + $context->register('CurrentNesting', $stack); + $context->register('InputZipper', $zipper); + $context->register('CurrentToken', $token); + + // -- begin INJECTOR -- + + $this->injectors = array(); + + $injectors = $config->getBatch('AutoFormat'); + $def_injectors = $definition->info_injector; + $custom_injectors = $injectors['Custom']; + unset($injectors['Custom']); // special case + foreach ($injectors as $injector => $b) { + // XXX: Fix with a legitimate lookup table of enabled filters + if (strpos($injector, '.') !== false) { + continue; + } + $injector = "HTMLPurifier_Injector_$injector"; + if (!$b) { + continue; + } + $this->injectors[] = new $injector; + } + foreach ($def_injectors as $injector) { + // assumed to be objects + $this->injectors[] = $injector; + } + foreach ($custom_injectors as $injector) { + if (!$injector) { + continue; + } + if (is_string($injector)) { + $injector = "HTMLPurifier_Injector_$injector"; + $injector = new $injector; + } + $this->injectors[] = $injector; + } + + // give the injectors references to the definition and context + // variables for performance reasons + foreach ($this->injectors as $ix => $injector) { + $error = $injector->prepare($config, $context); + if (!$error) { + continue; + } + array_splice($this->injectors, $ix, 1); // rm the injector + trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); + } + + // -- end INJECTOR -- + + // a note on reprocessing: + // In order to reduce code duplication, whenever some code needs + // to make HTML changes in order to make things "correct", the + // new HTML gets sent through the purifier, regardless of its + // status. This means that if we add a start token, because it + // was totally necessary, we don't have to update nesting; we just + // punt ($reprocess = true; continue;) and it does that for us. + + // isset is in loop because $tokens size changes during loop exec + for (;; + // only increment if we don't need to reprocess + $reprocess ? $reprocess = false : $token = $zipper->next($token)) { + + // check for a rewind + if (is_int($i)) { + // possibility: disable rewinding if the current token has a + // rewind set on it already. This would offer protection from + // infinite loop, but might hinder some advanced rewinding. + $rewind_offset = $this->injectors[$i]->getRewindOffset(); + if (is_int($rewind_offset)) { + for ($j = 0; $j < $rewind_offset; $j++) { + if (empty($zipper->front)) break; + $token = $zipper->prev($token); + // indicate that other injectors should not process this token, + // but we need to reprocess it. See Note [Injector skips] + unset($token->skip[$i]); + $token->rewind = $i; + if ($token instanceof HTMLPurifier_Token_Start) { + array_pop($this->stack); + } elseif ($token instanceof HTMLPurifier_Token_End) { + $this->stack[] = $token->start; + } + } + } + $i = false; + } + + // handle case of document end + if ($token === NULL) { + // kill processing if stack is empty + if (empty($this->stack)) { + break; + } + + // peek + $top_nesting = array_pop($this->stack); + $this->stack[] = $top_nesting; + + // send error [TagClosedSuppress] + if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); + } + + // append, don't splice, since this is the end + $token = new HTMLPurifier_Token_End($top_nesting->name); + + // punt! + $reprocess = true; + continue; + } + + //echo '
'; printZipper($zipper, $token);//printTokens($this->stack); + //flush(); + + // quick-check: if it's not a tag, no need to process + if (empty($token->is_tag)) { + if ($token instanceof HTMLPurifier_Token_Text) { + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) { + // See Note [Injector skips] + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + // XXX fuckup + $r = $token; + $injector->handleText($r); + $token = $this->processToken($r, $i); + $reprocess = true; + break; + } + } + // another possibility is a comment + continue; + } + + if (isset($definition->info[$token->name])) { + $type = $definition->info[$token->name]->child->type; + } else { + $type = false; // Type is unknown, treat accordingly + } + + // quick tag checks: anything that's *not* an end tag + $ok = false; + if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { + // claims to be a start tag but is empty + $token = new HTMLPurifier_Token_Empty( + $token->name, + $token->attr, + $token->line, + $token->col, + $token->armor + ); + $ok = true; + } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { + // claims to be empty but really is a start tag + // NB: this assignment is required + $old_token = $token; + $token = new HTMLPurifier_Token_End($token->name); + $token = $this->insertBefore( + new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor) + ); + // punt (since we had to modify the input stream in a non-trivial way) + $reprocess = true; + continue; + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + // real empty token + $ok = true; + } elseif ($token instanceof HTMLPurifier_Token_Start) { + // start tag + + // ...unless they also have to close their parent + if (!empty($this->stack)) { + + // Performance note: you might think that it's rather + // inefficient, recalculating the autoclose information + // for every tag that a token closes (since when we + // do an autoclose, we push a new token into the + // stream and then /process/ that, before + // re-processing this token.) But this is + // necessary, because an injector can make an + // arbitrary transformations to the autoclosing + // tokens we introduce, so things may have changed + // in the meantime. Also, doing the inefficient thing is + // "easy" to reason about (for certain perverse definitions + // of "easy") + + $parent = array_pop($this->stack); + $this->stack[] = $parent; + + $parent_def = null; + $parent_elements = null; + $autoclose = false; + if (isset($definition->info[$parent->name])) { + $parent_def = $definition->info[$parent->name]; + $parent_elements = $parent_def->child->getAllowedElements($config); + $autoclose = !isset($parent_elements[$token->name]); + } + + if ($autoclose && $definition->info[$token->name]->wrap) { + // Check if an element can be wrapped by another + // element to make it valid in a context (for + // example,
      needs a
    • in between) + $wrapname = $definition->info[$token->name]->wrap; + $wrapdef = $definition->info[$wrapname]; + $elements = $wrapdef->child->getAllowedElements($config); + if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { + $newtoken = new HTMLPurifier_Token_Start($wrapname); + $token = $this->insertBefore($newtoken); + $reprocess = true; + continue; + } + } + + $carryover = false; + if ($autoclose && $parent_def->formatting) { + $carryover = true; + } + + if ($autoclose) { + // check if this autoclose is doomed to fail + // (this rechecks $parent, which his harmless) + $autoclose_ok = isset($global_parent_allowed_elements[$token->name]); + if (!$autoclose_ok) { + foreach ($this->stack as $ancestor) { + $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config); + if (isset($elements[$token->name])) { + $autoclose_ok = true; + break; + } + if ($definition->info[$token->name]->wrap) { + $wrapname = $definition->info[$token->name]->wrap; + $wrapdef = $definition->info[$wrapname]; + $wrap_elements = $wrapdef->child->getAllowedElements($config); + if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) { + $autoclose_ok = true; + break; + } + } + } + } + if ($autoclose_ok) { + // errors need to be updated + $new_token = new HTMLPurifier_Token_End($parent->name); + $new_token->start = $parent; + // [TagClosedSuppress] + if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { + if (!$carryover) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); + } else { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); + } + } + if ($carryover) { + $element = clone $parent; + // [TagClosedAuto] + $element->armor['MakeWellFormed_TagClosedError'] = true; + $element->carryover = true; + $token = $this->processToken(array($new_token, $token, $element)); + } else { + $token = $this->insertBefore($new_token); + } + } else { + $token = $this->remove(); + } + $reprocess = true; + continue; + } + + } + $ok = true; + } + + if ($ok) { + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) { + // See Note [Injector skips] + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleElement($r); + $token = $this->processToken($r, $i); + $reprocess = true; + break; + } + if (!$reprocess) { + // ah, nothing interesting happened; do normal processing + if ($token instanceof HTMLPurifier_Token_Start) { + $this->stack[] = $token; + } elseif ($token instanceof HTMLPurifier_Token_End) { + throw new HTMLPurifier_Exception( + 'Improper handling of end tag in start code; possible error in MakeWellFormed' + ); + } + } + continue; + } + + // sanity check: we should be dealing with a closing tag + if (!$token instanceof HTMLPurifier_Token_End) { + throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); + } + + // make sure that we have something open + if (empty($this->stack)) { + if ($escape_invalid_tags) { + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); + } else { + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + } + $token = $this->remove(); + } + $reprocess = true; + continue; + } + + // first, check for the simplest case: everything closes neatly. + // Eventually, everything passes through here; if there are problems + // we modify the input stream accordingly and then punt, so that + // the tokens get processed again. + $current_parent = array_pop($this->stack); + if ($current_parent->name == $token->name) { + $token->start = $current_parent; + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) { + // See Note [Injector skips] + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleEnd($r); + $token = $this->processToken($r, $i); + $this->stack[] = $current_parent; + $reprocess = true; + break; + } + continue; + } + + // okay, so we're trying to close the wrong tag + + // undo the pop previous pop + $this->stack[] = $current_parent; + + // scroll back the entire nest, trying to find our tag. + // (feature could be to specify how far you'd like to go) + $size = count($this->stack); + // -2 because -1 is the last element, but we already checked that + $skipped_tags = false; + for ($j = $size - 2; $j >= 0; $j--) { + if ($this->stack[$j]->name == $token->name) { + $skipped_tags = array_slice($this->stack, $j); + break; + } + } + + // we didn't find the tag, so remove + if ($skipped_tags === false) { + if ($escape_invalid_tags) { + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); + } else { + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + } + $token = $this->remove(); + } + $reprocess = true; + continue; + } + + // do errors, in REVERSE $j order: a,b,c with + $c = count($skipped_tags); + if ($e) { + for ($j = $c - 1; $j > 0; $j--) { + // notice we exclude $j == 0, i.e. the current ending tag, from + // the errors... [TagClosedSuppress] + if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); + } + } + } + + // insert tags, in FORWARD $j order: c,b,a with + $replace = array($token); + for ($j = 1; $j < $c; $j++) { + // ...as well as from the insertions + $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); + $new_token->start = $skipped_tags[$j]; + array_unshift($replace, $new_token); + if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { + // [TagClosedAuto] + $element = clone $skipped_tags[$j]; + $element->carryover = true; + $element->armor['MakeWellFormed_TagClosedError'] = true; + $replace[] = $element; + } + } + $token = $this->processToken($replace); + $reprocess = true; + continue; + } + + $context->destroy('CurrentToken'); + $context->destroy('CurrentNesting'); + $context->destroy('InputZipper'); + + unset($this->injectors, $this->stack, $this->tokens); + return $zipper->toArray($token); + } + + /** + * Processes arbitrary token values for complicated substitution patterns. + * In general: + * + * If $token is an array, it is a list of tokens to substitute for the + * current token. These tokens then get individually processed. If there + * is a leading integer in the list, that integer determines how many + * tokens from the stream should be removed. + * + * If $token is a regular token, it is swapped with the current token. + * + * If $token is false, the current token is deleted. + * + * If $token is an integer, that number of tokens (with the first token + * being the current one) will be deleted. + * + * @param HTMLPurifier_Token|array|int|bool $token Token substitution value + * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if + * this is not an injector related operation. + * @throws HTMLPurifier_Exception + */ + protected function processToken($token, $injector = -1) + { + // Zend OpCache miscompiles $token = array($token), so + // avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108 + + // normalize forms of token + if (is_object($token)) { + $tmp = $token; + $token = array(1, $tmp); + } + if (is_int($token)) { + $tmp = $token; + $token = array($tmp); + } + if ($token === false) { + $token = array(1); + } + if (!is_array($token)) { + throw new HTMLPurifier_Exception('Invalid token type from injector'); + } + if (!is_int($token[0])) { + array_unshift($token, 1); + } + if ($token[0] === 0) { + throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + } + + // $token is now an array with the following form: + // array(number nodes to delete, new node 1, new node 2, ...) + + $delete = array_shift($token); + list($old, $r) = $this->zipper->splice($this->token, $delete, $token); + + if ($injector > -1) { + // See Note [Injector skips] + // Determine appropriate skips. Here's what the code does: + // *If* we deleted one or more tokens, copy the skips + // of those tokens into the skips of the new tokens (in $token). + // Also, mark the newly inserted tokens as having come from + // $injector. + $oldskip = isset($old[0]) ? $old[0]->skip : array(); + foreach ($token as $object) { + $object->skip = $oldskip; + $object->skip[$injector] = true; + } + } + + return $r; + + } + + /** + * Inserts a token before the current token. Cursor now points to + * this token. You must reprocess after this. + * @param HTMLPurifier_Token $token + */ + private function insertBefore($token) + { + // NB not $this->zipper->insertBefore(), due to positioning + // differences + $splice = $this->zipper->splice($this->token, 0, array($token)); + + return $splice[1]; + } + + /** + * Removes current token. Cursor now points to new token occupying previously + * occupied space. You must reprocess after this. + */ + private function remove() + { + return $this->zipper->delete(); + } +} + +// Note [Injector skips] +// ~~~~~~~~~~~~~~~~~~~~~ +// When I originally designed this class, the idea behind the 'skip' +// property of HTMLPurifier_Token was to help avoid infinite loops +// in injector processing. For example, suppose you wrote an injector +// that bolded swear words. Naively, you might write it so that +// whenever you saw ****, you replaced it with ****. +// +// When this happens, we will reprocess all of the tokens with the +// other injectors. Now there is an opportunity for infinite loop: +// if we rerun the swear-word injector on these tokens, we might +// see **** and then reprocess again to get +// **** ad infinitum. +// +// Thus, the idea of a skip is that once we process a token with +// an injector, we mark all of those tokens as having "come from" +// the injector, and we never run the injector again on these +// tokens. +// +// There were two more complications, however: +// +// - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if +// you had , after you removed the , you +// really would like this injector to go back and reprocess +// the tag, discovering that it is now empty and can be +// removed. So we reintroduced the possibility of infinite looping +// by adding a "rewind" function, which let you go back to an +// earlier point in the token stream and reprocess it with injectors. +// Needless to say, we need to UN-skip the token so it gets +// reprocessed. +// +// - Suppose that you successfuly process a token, replace it with +// one with your skip mark, but now another injector wants to +// process the skipped token with another token. Should you continue +// to skip that new token, or reprocess it? If you reprocess, +// you can end up with an infinite loop where one injector converts +// to , and then another injector converts it back. So +// we inherit the skips, but for some reason, I thought that we +// should inherit the skip from the first token of the token +// that we deleted. Why? Well, it seems to work OK. +// +// If I were to redesign this functionality, I would absolutely not +// go about doing it this way: the semantics are just not very well +// defined, and in any case you probably wanted to operate on trees, +// not token streams. + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php new file mode 100644 index 00000000000..1a8149eccb4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php @@ -0,0 +1,207 @@ +getHTMLDefinition(); + $generator = new HTMLPurifier_Generator($config, $context); + $result = array(); + + $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); + $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); + + // currently only used to determine if comments should be kept + $trusted = $config->get('HTML.Trusted'); + $comment_lookup = $config->get('HTML.AllowedComments'); + $comment_regexp = $config->get('HTML.AllowedCommentsRegexp'); + $check_comments = $comment_lookup !== array() || $comment_regexp !== null; + + $remove_script_contents = $config->get('Core.RemoveScriptContents'); + $hidden_elements = $config->get('Core.HiddenElements'); + + // remove script contents compatibility + if ($remove_script_contents === true) { + $hidden_elements['script'] = true; + } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) { + unset($hidden_elements['script']); + } + + $attr_validator = new HTMLPurifier_AttrValidator(); + + // removes tokens until it reaches a closing tag with its value + $remove_until = false; + + // converts comments into text tokens when this is equal to a tag name + $textify_comments = false; + + $token = false; + $context->register('CurrentToken', $token); + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + foreach ($tokens as $token) { + if ($remove_until) { + if (empty($token->is_tag) || $token->name !== $remove_until) { + continue; + } + } + if (!empty($token->is_tag)) { + // DEFINITION CALL + + // before any processing, try to transform the element + if (isset($definition->info_tag_transform[$token->name])) { + $original_name = $token->name; + // there is a transformation for this tag + // DEFINITION CALL + $token = $definition-> + info_tag_transform[$token->name]->transform($token, $config, $context); + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + } + } + + if (isset($definition->info[$token->name])) { + // mostly everything's good, but + // we need to make sure required attributes are in order + if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && + $definition->info[$token->name]->required_attr && + ($token->name != 'img' || $remove_invalid_img) // ensure config option still works + ) { + $attr_validator->validateToken($token, $config, $context); + $ok = true; + foreach ($definition->info[$token->name]->required_attr as $name) { + if (!isset($token->attr[$name])) { + $ok = false; + break; + } + } + if (!$ok) { + if ($e) { + $e->send( + E_ERROR, + 'Strategy_RemoveForeignElements: Missing required attribute', + $name + ); + } + continue; + } + $token->armor['ValidateAttributes'] = true; + } + + if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) { + $textify_comments = $token->name; + } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) { + $textify_comments = false; + } + + } elseif ($escape_invalid_tags) { + // invalid tag, generate HTML representation and insert in + if ($e) { + $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + } + $token = new HTMLPurifier_Token_Text( + $generator->generateFromToken($token) + ); + } else { + // check if we need to destroy all of the tag's children + // CAN BE GENERICIZED + if (isset($hidden_elements[$token->name])) { + if ($token instanceof HTMLPurifier_Token_Start) { + $remove_until = $token->name; + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + // do nothing: we're still looking + } else { + $remove_until = false; + } + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + } + } else { + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + } + } + continue; + } + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + // textify comments in script tags when they are allowed + if ($textify_comments !== false) { + $data = $token->data; + $token = new HTMLPurifier_Token_Text($data); + } elseif ($trusted || $check_comments) { + // always cleanup comments + $trailing_hyphen = false; + if ($e) { + // perform check whether or not there's a trailing hyphen + if (substr($token->data, -1) == '-') { + $trailing_hyphen = true; + } + } + $token->data = rtrim($token->data, '-'); + $found_double_hyphen = false; + while (strpos($token->data, '--') !== false) { + $found_double_hyphen = true; + $token->data = str_replace('--', '-', $token->data); + } + if ($trusted || !empty($comment_lookup[trim($token->data)]) || + ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) { + // OK good + if ($e) { + if ($trailing_hyphen) { + $e->send( + E_NOTICE, + 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' + ); + } + if ($found_double_hyphen) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); + } + } + } else { + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + } + continue; + } + } else { + // strip comments + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + } + continue; + } + } elseif ($token instanceof HTMLPurifier_Token_Text) { + } else { + continue; + } + $result[] = $token; + } + if ($remove_until && $e) { + // we removed tokens until the end, throw error + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); + } + $context->destroy('CurrentToken'); + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php new file mode 100644 index 00000000000..fbb3d27c812 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php @@ -0,0 +1,45 @@ +register('CurrentToken', $token); + + foreach ($tokens as $key => $token) { + + // only process tokens that have attributes, + // namely start and empty tags + if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) { + continue; + } + + // skip tokens that are armored + if (!empty($token->armor['ValidateAttributes'])) { + continue; + } + + // note that we have no facilities here for removing tokens + $validator->validateToken($token, $config, $context); + } + $context->destroy('CurrentToken'); + return $tokens; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php new file mode 100644 index 00000000000..c07370197a9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php @@ -0,0 +1,47 @@ +accessed[$index] = true; + return parent::offsetGet($index); + } + + /** + * Returns a lookup array of all array indexes that have been accessed. + * @return array in form array($index => true). + */ + public function getAccessed() + { + return $this->accessed; + } + + /** + * Resets the access array. + */ + public function resetAccessed() + { + $this->accessed = array(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php new file mode 100644 index 00000000000..7c73f808355 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php @@ -0,0 +1,136 @@ + 'DefaultKeyValue', + * 'KEY' => 'Value', + * 'KEY2' => 'Value2', + * 'MULTILINE-KEY' => "Multiline\nvalue.\n", + * ) + * + * We use this as an easy to use file-format for configuration schema + * files, but the class itself is usage agnostic. + * + * You can use ---- to forcibly terminate parsing of a single string-hash; + * this marker is used in multi string-hashes to delimit boundaries. + */ +class HTMLPurifier_StringHashParser +{ + + /** + * @type string + */ + public $default = 'ID'; + + /** + * Parses a file that contains a single string-hash. + * @param string $file + * @return array + */ + public function parseFile($file) + { + if (!file_exists($file)) { + return false; + } + $fh = fopen($file, 'r'); + if (!$fh) { + return false; + } + $ret = $this->parseHandle($fh); + fclose($fh); + return $ret; + } + + /** + * Parses a file that contains multiple string-hashes delimited by '----' + * @param string $file + * @return array + */ + public function parseMultiFile($file) + { + if (!file_exists($file)) { + return false; + } + $ret = array(); + $fh = fopen($file, 'r'); + if (!$fh) { + return false; + } + while (!feof($fh)) { + $ret[] = $this->parseHandle($fh); + } + fclose($fh); + return $ret; + } + + /** + * Internal parser that acepts a file handle. + * @note While it's possible to simulate in-memory parsing by using + * custom stream wrappers, if such a use-case arises we should + * factor out the file handle into its own class. + * @param resource $fh File handle with pointer at start of valid string-hash + * block. + * @return array + */ + protected function parseHandle($fh) + { + $state = false; + $single = false; + $ret = array(); + do { + $line = fgets($fh); + if ($line === false) { + break; + } + $line = rtrim($line, "\n\r"); + if (!$state && $line === '') { + continue; + } + if ($line === '----') { + break; + } + if (strncmp('--#', $line, 3) === 0) { + // Comment + continue; + } elseif (strncmp('--', $line, 2) === 0) { + // Multiline declaration + $state = trim($line, '- '); + if (!isset($ret[$state])) { + $ret[$state] = ''; + } + continue; + } elseif (!$state) { + $single = true; + if (strpos($line, ':') !== false) { + // Single-line declaration + list($state, $line) = explode(':', $line, 2); + $line = trim($line); + } else { + // Use default declaration + $state = $this->default; + } + } + if ($single) { + $ret[$state] = $line; + $single = false; + $state = false; + } else { + $ret[$state] .= "$line\n"; + } + } while (!feof($fh)); + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php new file mode 100644 index 00000000000..7b8d833433a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php @@ -0,0 +1,37 @@ + 'xx-small', + '1' => 'xx-small', + '2' => 'small', + '3' => 'medium', + '4' => 'large', + '5' => 'x-large', + '6' => 'xx-large', + '7' => '300%', + '-1' => 'smaller', + '-2' => '60%', + '+1' => 'larger', + '+2' => '150%', + '+3' => '200%', + '+4' => '300%' + ); + + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token_End|string + */ + public function transform($tag, $config, $context) + { + if ($tag instanceof HTMLPurifier_Token_End) { + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + return $new_tag; + } + + $attr = $tag->attr; + $prepend_style = ''; + + // handle color transform + if (isset($attr['color'])) { + $prepend_style .= 'color:' . $attr['color'] . ';'; + unset($attr['color']); + } + + // handle face transform + if (isset($attr['face'])) { + $prepend_style .= 'font-family:' . $attr['face'] . ';'; + unset($attr['face']); + } + + // handle size transform + if (isset($attr['size'])) { + // normalize large numbers + if ($attr['size'] !== '') { + if ($attr['size'][0] == '+' || $attr['size'][0] == '-') { + $size = (int)$attr['size']; + if ($size < -2) { + $attr['size'] = '-2'; + } + if ($size > 4) { + $attr['size'] = '+4'; + } + } else { + $size = (int)$attr['size']; + if ($size > 7) { + $attr['size'] = '7'; + } + } + } + if (isset($this->_size_lookup[$attr['size']])) { + $prepend_style .= 'font-size:' . + $this->_size_lookup[$attr['size']] . ';'; + } + unset($attr['size']); + } + + if ($prepend_style) { + $attr['style'] = isset($attr['style']) ? + $prepend_style . $attr['style'] : + $prepend_style; + } + + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + $new_tag->attr = $attr; + + return $new_tag; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php new file mode 100644 index 00000000000..71bf10b91f2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php @@ -0,0 +1,44 @@ +transform_to = $transform_to; + $this->style = $style; + } + + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function transform($tag, $config, $context) + { + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + if (!is_null($this->style) && + ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty) + ) { + $this->prependCSS($new_tag->attr, $this->style); + } + return $new_tag; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php new file mode 100644 index 00000000000..84d3619a367 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php @@ -0,0 +1,100 @@ +line = $l; + $this->col = $c; + } + + /** + * Convenience function for DirectLex settings line/col position. + * @param int $l + * @param int $c + */ + public function rawPosition($l, $c) + { + if ($c === -1) { + $l++; + } + $this->line = $l; + $this->col = $c; + } + + /** + * Converts a token into its corresponding node. + */ + abstract public function toNode(); +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php new file mode 100644 index 00000000000..23453c70527 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php @@ -0,0 +1,38 @@ +data = $data; + $this->line = $line; + $this->col = $col; + } + + public function toNode() { + return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php new file mode 100644 index 00000000000..78a95f55555 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php @@ -0,0 +1,15 @@ +empty = true; + return $n; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php new file mode 100644 index 00000000000..59b38fdc571 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php @@ -0,0 +1,24 @@ +toNode not supported!"); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php new file mode 100644 index 00000000000..019f317ada4 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php @@ -0,0 +1,10 @@ +!empty($obj->is_tag) + * without having to use a function call is_a(). + * @type bool + */ + public $is_tag = true; + + /** + * The lower-case name of the tag, like 'a', 'b' or 'blockquote'. + * + * @note Strictly speaking, XML tags are case sensitive, so we shouldn't + * be lower-casing them, but these tokens cater to HTML tags, which are + * insensitive. + * @type string + */ + public $name; + + /** + * Associative array of the tag's attributes. + * @type array + */ + public $attr = array(); + + /** + * Non-overloaded constructor, which lower-cases passed tag name. + * + * @param string $name String name. + * @param array $attr Associative array of attributes. + * @param int $line + * @param int $col + * @param array $armor + */ + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) + { + $this->name = ctype_lower($name) ? $name : strtolower($name); + foreach ($attr as $key => $value) { + // normalization only necessary when key is not lowercase + if (!ctype_lower($key)) { + $new_key = strtolower($key); + if (!isset($attr[$new_key])) { + $attr[$new_key] = $attr[$key]; + } + if ($new_key !== $key) { + unset($attr[$key]); + } + } + } + $this->attr = $attr; + $this->line = $line; + $this->col = $col; + $this->armor = $armor; + } + + public function toNode() { + return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php new file mode 100644 index 00000000000..f26a1c211ba --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php @@ -0,0 +1,53 @@ +data = $data; + $this->is_whitespace = ctype_space($data); + $this->line = $line; + $this->col = $col; + } + + public function toNode() { + return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php new file mode 100644 index 00000000000..dea2446b93a --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php @@ -0,0 +1,118 @@ +p_start = new HTMLPurifier_Token_Start('', array()); + $this->p_end = new HTMLPurifier_Token_End(''); + $this->p_empty = new HTMLPurifier_Token_Empty('', array()); + $this->p_text = new HTMLPurifier_Token_Text(''); + $this->p_comment = new HTMLPurifier_Token_Comment(''); + } + + /** + * Creates a HTMLPurifier_Token_Start. + * @param string $name Tag name + * @param array $attr Associative array of attributes + * @return HTMLPurifier_Token_Start Generated HTMLPurifier_Token_Start + */ + public function createStart($name, $attr = array()) + { + $p = clone $this->p_start; + $p->__construct($name, $attr); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_End. + * @param string $name Tag name + * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End + */ + public function createEnd($name) + { + $p = clone $this->p_end; + $p->__construct($name); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Empty. + * @param string $name Tag name + * @param array $attr Associative array of attributes + * @return HTMLPurifier_Token_Empty Generated HTMLPurifier_Token_Empty + */ + public function createEmpty($name, $attr = array()) + { + $p = clone $this->p_empty; + $p->__construct($name, $attr); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Text. + * @param string $data Data of text token + * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text + */ + public function createText($data) + { + $p = clone $this->p_text; + $p->__construct($data); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Comment. + * @param string $data Data of comment token + * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment + */ + public function createComment($data) + { + $p = clone $this->p_comment; + $p->__construct($data); + return $p; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php new file mode 100644 index 00000000000..9c5be39d188 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php @@ -0,0 +1,316 @@ +scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme); + $this->userinfo = $userinfo; + $this->host = $host; + $this->port = is_null($port) ? $port : (int)$port; + $this->path = $path; + $this->query = $query; + $this->fragment = $fragment; + } + + /** + * Retrieves a scheme object corresponding to the URI's scheme/default + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI + */ + public function getSchemeObj($config, $context) + { + $registry = HTMLPurifier_URISchemeRegistry::instance(); + if ($this->scheme !== null) { + $scheme_obj = $registry->getScheme($this->scheme, $config, $context); + if (!$scheme_obj) { + return false; + } // invalid scheme, clean it out + } else { + // no scheme: retrieve the default one + $def = $config->getDefinition('URI'); + $scheme_obj = $def->getDefaultScheme($config, $context); + if (!$scheme_obj) { + if ($def->defaultScheme !== null) { + // something funky happened to the default scheme object + trigger_error( + 'Default scheme object "' . $def->defaultScheme . '" was not readable', + E_USER_WARNING + ); + } // suppress error if it's null + return false; + } + } + return $scheme_obj; + } + + /** + * Generic validation method applicable for all schemes. May modify + * this URI in order to get it into a compliant form. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool True if validation/filtering succeeds, false if failure + */ + public function validate($config, $context) + { + // ABNF definitions from RFC 3986 + $chars_sub_delims = '!$&\'()*+,;='; + $chars_gen_delims = ':/?#[]@'; + $chars_pchar = $chars_sub_delims . ':@'; + + // validate host + if (!is_null($this->host)) { + $host_def = new HTMLPurifier_AttrDef_URI_Host(); + $this->host = $host_def->validate($this->host, $config, $context); + if ($this->host === false) { + $this->host = null; + } + } + + // validate scheme + // NOTE: It's not appropriate to check whether or not this + // scheme is in our registry, since a URIFilter may convert a + // URI that we don't allow into one we do. So instead, we just + // check if the scheme can be dropped because there is no host + // and it is our default scheme. + if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') { + // support for relative paths is pretty abysmal when the + // scheme is present, so axe it when possible + $def = $config->getDefinition('URI'); + if ($def->defaultScheme === $this->scheme) { + $this->scheme = null; + } + } + + // validate username + if (!is_null($this->userinfo)) { + $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':'); + $this->userinfo = $encoder->encode($this->userinfo); + } + + // validate port + if (!is_null($this->port)) { + if ($this->port < 1 || $this->port > 65535) { + $this->port = null; + } + } + + // validate path + $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/'); + if (!is_null($this->host)) { // this catches $this->host === '' + // path-abempty (hier and relative) + // http://www.example.com/my/path + // //www.example.com/my/path (looks odd, but works, and + // recognized by most browsers) + // (this set is valid or invalid on a scheme by scheme + // basis, so we'll deal with it later) + // file:///my/path + // ///my/path + $this->path = $segments_encoder->encode($this->path); + } elseif ($this->path !== '') { + if ($this->path[0] === '/') { + // path-absolute (hier and relative) + // http:/my/path + // /my/path + if (strlen($this->path) >= 2 && $this->path[1] === '/') { + // This could happen if both the host gets stripped + // out + // http://my/path + // //my/path + $this->path = ''; + } else { + $this->path = $segments_encoder->encode($this->path); + } + } elseif (!is_null($this->scheme)) { + // path-rootless (hier) + // http:my/path + // Short circuit evaluation means we don't need to check nz + $this->path = $segments_encoder->encode($this->path); + } else { + // path-noscheme (relative) + // my/path + // (once again, not checking nz) + $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@'); + $c = strpos($this->path, '/'); + if ($c !== false) { + $this->path = + $segment_nc_encoder->encode(substr($this->path, 0, $c)) . + $segments_encoder->encode(substr($this->path, $c)); + } else { + $this->path = $segment_nc_encoder->encode($this->path); + } + } + } else { + // path-empty (hier and relative) + $this->path = ''; // just to be safe + } + + // qf = query and fragment + $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?'); + + if (!is_null($this->query)) { + $this->query = $qf_encoder->encode($this->query); + } + + if (!is_null($this->fragment)) { + $this->fragment = $qf_encoder->encode($this->fragment); + } + return true; + } + + /** + * Convert URI back to string + * @return string URI appropriate for output + */ + public function toString() + { + // reconstruct authority + $authority = null; + // there is a rendering difference between a null authority + // (http:foo-bar) and an empty string authority + // (http:///foo-bar). + if (!is_null($this->host)) { + $authority = ''; + if (!is_null($this->userinfo)) { + $authority .= $this->userinfo . '@'; + } + $authority .= $this->host; + if (!is_null($this->port)) { + $authority .= ':' . $this->port; + } + } + + // Reconstruct the result + // One might wonder about parsing quirks from browsers after + // this reconstruction. Unfortunately, parsing behavior depends + // on what *scheme* was employed (file:///foo is handled *very* + // differently than http:///foo), so unfortunately we have to + // defer to the schemes to do the right thing. + $result = ''; + if (!is_null($this->scheme)) { + $result .= $this->scheme . ':'; + } + if (!is_null($authority)) { + $result .= '//' . $authority; + } + $result .= $this->path; + if (!is_null($this->query)) { + $result .= '?' . $this->query; + } + if (!is_null($this->fragment)) { + $result .= '#' . $this->fragment; + } + + return $result; + } + + /** + * Returns true if this URL might be considered a 'local' URL given + * the current context. This is true when the host is null, or + * when it matches the host supplied to the configuration. + * + * Note that this does not do any scheme checking, so it is mostly + * only appropriate for metadata that doesn't care about protocol + * security. isBenign is probably what you actually want. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function isLocal($config, $context) + { + if ($this->host === null) { + return true; + } + $uri_def = $config->getDefinition('URI'); + if ($uri_def->host === $this->host) { + return true; + } + return false; + } + + /** + * Returns true if this URL should be considered a 'benign' URL, + * that is: + * + * - It is a local URL (isLocal), and + * - It has a equal or better level of security + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function isBenign($config, $context) + { + if (!$this->isLocal($config, $context)) { + return false; + } + + $scheme_obj = $this->getSchemeObj($config, $context); + if (!$scheme_obj) { + return false; + } // conservative approach + + $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context); + if ($current_scheme_obj->secure) { + if (!$scheme_obj->secure) { + return false; + } + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php new file mode 100644 index 00000000000..e0bd8bcca88 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php @@ -0,0 +1,112 @@ +registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); + $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); + $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources()); + $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); + $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe()); + $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); + $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); + } + + public function registerFilter($filter) + { + $this->registeredFilters[$filter->name] = $filter; + } + + public function addFilter($filter, $config) + { + $r = $filter->prepare($config); + if ($r === false) return; // null is ok, for backwards compat + if ($filter->post) { + $this->postFilters[$filter->name] = $filter; + } else { + $this->filters[$filter->name] = $filter; + } + } + + protected function doSetup($config) + { + $this->setupMemberVariables($config); + $this->setupFilters($config); + } + + protected function setupFilters($config) + { + foreach ($this->registeredFilters as $name => $filter) { + if ($filter->always_load) { + $this->addFilter($filter, $config); + } else { + $conf = $config->get('URI.' . $name); + if ($conf !== false && $conf !== null) { + $this->addFilter($filter, $config); + } + } + } + unset($this->registeredFilters); + } + + protected function setupMemberVariables($config) + { + $this->host = $config->get('URI.Host'); + $base_uri = $config->get('URI.Base'); + if (!is_null($base_uri)) { + $parser = new HTMLPurifier_URIParser(); + $this->base = $parser->parse($base_uri); + $this->defaultScheme = $this->base->scheme; + if (is_null($this->host)) $this->host = $this->base->host; + } + if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); + } + + public function getDefaultScheme($config, $context) + { + return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context); + } + + public function filter(&$uri, $config, $context) + { + foreach ($this->filters as $name => $f) { + $result = $f->filter($uri, $config, $context); + if (!$result) return false; + } + return true; + } + + public function postFilter(&$uri, $config, $context) + { + foreach ($this->postFilters as $name => $f) { + $result = $f->filter($uri, $config, $context); + if (!$result) return false; + } + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php new file mode 100644 index 00000000000..09724e9f418 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php @@ -0,0 +1,74 @@ +getDefinition('URI')->host; + if ($our_host !== null) { + $this->ourHostParts = array_reverse(explode('.', $our_host)); + } + } + + /** + * @param HTMLPurifier_URI $uri Reference + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (is_null($uri->host)) { + return true; + } + if ($this->ourHostParts === false) { + return false; + } + $host_parts = array_reverse(explode('.', $uri->host)); + foreach ($this->ourHostParts as $i => $x) { + if (!isset($host_parts[$i])) { + return false; + } + if ($host_parts[$i] != $this->ourHostParts[$i]) { + return false; + } + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php new file mode 100644 index 00000000000..c6562169e05 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php @@ -0,0 +1,25 @@ +get('EmbeddedURI', true)) { + return true; + } + return parent::filter($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php new file mode 100644 index 00000000000..d5c412c4446 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php @@ -0,0 +1,22 @@ +get('EmbeddedURI', true); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php new file mode 100644 index 00000000000..a6645c17ee6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php @@ -0,0 +1,46 @@ +blacklist = $config->get('URI.HostBlacklist'); + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + foreach ($this->blacklist as $blacklisted_host_fragment) { + if (strpos($uri->host, $blacklisted_host_fragment) !== false) { + return false; + } + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php new file mode 100644 index 00000000000..c507bbff8e2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php @@ -0,0 +1,158 @@ +getDefinition('URI'); + $this->base = $def->base; + if (is_null($this->base)) { + trigger_error( + 'URI.MakeAbsolute is being ignored due to lack of ' . + 'value for URI.Base configuration', + E_USER_WARNING + ); + return false; + } + $this->base->fragment = null; // fragment is invalid for base URI + $stack = explode('/', $this->base->path); + array_pop($stack); // discard last segment + $stack = $this->_collapseStack($stack); // do pre-parsing + $this->basePathStack = $stack; + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (is_null($this->base)) { + return true; + } // abort early + if ($uri->path === '' && is_null($uri->scheme) && + is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) { + // reference to current document + $uri = clone $this->base; + return true; + } + if (!is_null($uri->scheme)) { + // absolute URI already: don't change + if (!is_null($uri->host)) { + return true; + } + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) { + // scheme not recognized + return false; + } + if (!$scheme_obj->hierarchical) { + // non-hierarchal URI with explicit scheme, don't change + return true; + } + // special case: had a scheme but always is hierarchical and had no authority + } + if (!is_null($uri->host)) { + // network path, don't bother + return true; + } + if ($uri->path === '') { + $uri->path = $this->base->path; + } elseif ($uri->path[0] !== '/') { + // relative path, needs more complicated processing + $stack = explode('/', $uri->path); + $new_stack = array_merge($this->basePathStack, $stack); + if ($new_stack[0] !== '' && !is_null($this->base->host)) { + array_unshift($new_stack, ''); + } + $new_stack = $this->_collapseStack($new_stack); + $uri->path = implode('/', $new_stack); + } else { + // absolute path, but still we should collapse + $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path))); + } + // re-combine + $uri->scheme = $this->base->scheme; + if (is_null($uri->userinfo)) { + $uri->userinfo = $this->base->userinfo; + } + if (is_null($uri->host)) { + $uri->host = $this->base->host; + } + if (is_null($uri->port)) { + $uri->port = $this->base->port; + } + return true; + } + + /** + * Resolve dots and double-dots in a path stack + * @param array $stack + * @return array + */ + private function _collapseStack($stack) + { + $result = array(); + $is_folder = false; + for ($i = 0; isset($stack[$i]); $i++) { + $is_folder = false; + // absorb an internally duplicated slash + if ($stack[$i] == '' && $i && isset($stack[$i + 1])) { + continue; + } + if ($stack[$i] == '..') { + if (!empty($result)) { + $segment = array_pop($result); + if ($segment === '' && empty($result)) { + // error case: attempted to back out too far: + // restore the leading slash + $result[] = ''; + } elseif ($segment === '..') { + $result[] = '..'; // cannot remove .. with .. + } + } else { + // relative path, preserve the double-dots + $result[] = '..'; + } + $is_folder = true; + continue; + } + if ($stack[$i] == '.') { + // silently absorb + $is_folder = true; + continue; + } + $result[] = $stack[$i]; + } + if ($is_folder) { + $result[] = ''; + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php new file mode 100644 index 00000000000..6e03315a17b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php @@ -0,0 +1,115 @@ +target = $config->get('URI.' . $this->name); + $this->parser = new HTMLPurifier_URIParser(); + $this->doEmbed = $config->get('URI.MungeResources'); + $this->secretKey = $config->get('URI.MungeSecretKey'); + if ($this->secretKey && !function_exists('hash_hmac')) { + throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support."); + } + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if ($context->get('EmbeddedURI', true) && !$this->doEmbed) { + return true; + } + + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) { + return true; + } // ignore unknown schemes, maybe another postfilter did it + if (!$scheme_obj->browsable) { + return true; + } // ignore non-browseable schemes, since we can't munge those in a reasonable way + if ($uri->isBenign($config, $context)) { + return true; + } // don't redirect if a benign URL + + $this->makeReplace($uri, $config, $context); + $this->replace = array_map('rawurlencode', $this->replace); + + $new_uri = strtr($this->target, $this->replace); + $new_uri = $this->parser->parse($new_uri); + // don't redirect if the target host is the same as the + // starting host + if ($uri->host === $new_uri->host) { + return true; + } + $uri = $new_uri; // overwrite + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + */ + protected function makeReplace($uri, $config, $context) + { + $string = $uri->toString(); + // always available + $this->replace['%s'] = $string; + $this->replace['%r'] = $context->get('EmbeddedURI', true); + $token = $context->get('CurrentToken', true); + $this->replace['%n'] = $token ? $token->name : null; + $this->replace['%m'] = $context->get('CurrentAttr', true); + $this->replace['%p'] = $context->get('CurrentCSSProperty', true); + // not always available + if ($this->secretKey) { + $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php new file mode 100644 index 00000000000..f609c47a342 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php @@ -0,0 +1,68 @@ +regexp = $config->get('URI.SafeIframeRegexp'); + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + // check if filter not applicable + if (!$config->get('HTML.SafeIframe')) { + return true; + } + // check if the filter should actually trigger + if (!$context->get('EmbeddedURI', true)) { + return true; + } + $token = $context->get('CurrentToken', true); + if (!($token && $token->name == 'iframe')) { + return true; + } + // check if we actually have some whitelists enabled + if ($this->regexp === null) { + return false; + } + // actually check the whitelists + return preg_match($this->regexp, $uri->toString()); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php new file mode 100644 index 00000000000..0e7381a07b0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php @@ -0,0 +1,71 @@ +percentEncoder = new HTMLPurifier_PercentEncoder(); + } + + /** + * Parses a URI. + * @param $uri string URI to parse + * @return HTMLPurifier_URI representation of URI. This representation has + * not been validated yet and may not conform to RFC. + */ + public function parse($uri) + { + $uri = $this->percentEncoder->normalize($uri); + + // Regexp is as per Appendix B. + // Note that ["<>] are an addition to the RFC's recommended + // characters, because they represent external delimeters. + $r_URI = '!'. + '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme + '(//([^/?#"<>]*))?'. // 4. Authority + '([^?#"<>]*)'. // 5. Path + '(\?([^#"<>]*))?'. // 7. Query + '(#([^"<>]*))?'. // 8. Fragment + '!'; + + $matches = array(); + $result = preg_match($r_URI, $uri, $matches); + + if (!$result) return false; // *really* invalid URI + + // seperate out parts + $scheme = !empty($matches[1]) ? $matches[2] : null; + $authority = !empty($matches[3]) ? $matches[4] : null; + $path = $matches[5]; // always present, can be empty + $query = !empty($matches[6]) ? $matches[7] : null; + $fragment = !empty($matches[8]) ? $matches[9] : null; + + // further parse authority + if ($authority !== null) { + $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/"; + $matches = array(); + preg_match($r_authority, $authority, $matches); + $userinfo = !empty($matches[1]) ? $matches[2] : null; + $host = !empty($matches[3]) ? $matches[3] : ''; + $port = !empty($matches[4]) ? (int) $matches[5] : null; + } else { + $port = $host = $userinfo = null; + } + + return new HTMLPurifier_URI( + $scheme, $userinfo, $host, $port, $path, $query, $fragment); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php new file mode 100644 index 00000000000..fe9e82cf261 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php @@ -0,0 +1,102 @@ +, resolves edge cases + * with making relative URIs absolute + * @type bool + */ + public $hierarchical = false; + + /** + * Whether or not the URI may omit a hostname when the scheme is + * explicitly specified, ala file:///path/to/file. As of writing, + * 'file' is the only scheme that browsers support his properly. + * @type bool + */ + public $may_omit_host = false; + + /** + * Validates the components of a URI for a specific scheme. + * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool success or failure + */ + abstract public function doValidate(&$uri, $config, $context); + + /** + * Public interface for validating components of a URI. Performs a + * bunch of default actions. Don't overload this method. + * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool success or failure + */ + public function validate(&$uri, $config, $context) + { + if ($this->default_port == $uri->port) { + $uri->port = null; + } + // kludge: browsers do funny things when the scheme but not the + // authority is set + if (!$this->may_omit_host && + // if the scheme is present, a missing host is always in error + (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) || + // if the scheme is not present, a *blank* host is in error, + // since this translates into '///path' which most browsers + // interpret as being 'http://path'. + (is_null($uri->scheme) && $uri->host === '') + ) { + do { + if (is_null($uri->scheme)) { + if (substr($uri->path, 0, 2) != '//') { + $uri->host = null; + break; + } + // URI is '////path', so we cannot nullify the + // host to preserve semantics. Try expanding the + // hostname instead (fall through) + } + // first see if we can manually insert a hostname + $host = $config->get('URI.Host'); + if (!is_null($host)) { + $uri->host = $host; + } else { + // we can't do anything sensible, reject the URL. + return false; + } + } while (false); + } + return $this->doValidate($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php new file mode 100644 index 00000000000..41c49d5533f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php @@ -0,0 +1,136 @@ + true, + 'image/gif' => true, + 'image/png' => true, + ); + // this is actually irrelevant since we only write out the path + // component + /** + * @type bool + */ + public $may_omit_host = true; + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { + $result = explode(',', $uri->path, 2); + $is_base64 = false; + $charset = null; + $content_type = null; + if (count($result) == 2) { + list($metadata, $data) = $result; + // do some legwork on the metadata + $metas = explode(';', $metadata); + while (!empty($metas)) { + $cur = array_shift($metas); + if ($cur == 'base64') { + $is_base64 = true; + break; + } + if (substr($cur, 0, 8) == 'charset=') { + // doesn't match if there are arbitrary spaces, but + // whatever dude + if ($charset !== null) { + continue; + } // garbage + $charset = substr($cur, 8); // not used + } else { + if ($content_type !== null) { + continue; + } // garbage + $content_type = $cur; + } + } + } else { + $data = $result[0]; + } + if ($content_type !== null && empty($this->allowed_types[$content_type])) { + return false; + } + if ($charset !== null) { + // error; we don't allow plaintext stuff + $charset = null; + } + $data = rawurldecode($data); + if ($is_base64) { + $raw_data = base64_decode($data); + } else { + $raw_data = $data; + } + if ( strlen($raw_data) < 12 ) { + // error; exif_imagetype throws exception with small files, + // and this likely indicates a corrupt URI/failed parse anyway + return false; + } + // XXX probably want to refactor this into a general mechanism + // for filtering arbitrary content types + if (function_exists('sys_get_temp_dir')) { + $file = tempnam(sys_get_temp_dir(), ""); + } else { + $file = tempnam("/tmp", ""); + } + file_put_contents($file, $raw_data); + if (function_exists('exif_imagetype')) { + $image_code = exif_imagetype($file); + unlink($file); + } elseif (function_exists('getimagesize')) { + set_error_handler(array($this, 'muteErrorHandler')); + $info = getimagesize($file); + restore_error_handler(); + unlink($file); + if ($info == false) { + return false; + } + $image_code = $info[2]; + } else { + trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR); + } + $real_content_type = image_type_to_mime_type($image_code); + if ($real_content_type != $content_type) { + // we're nice guys; if the content type is something else we + // support, change it over + if (empty($this->allowed_types[$real_content_type])) { + return false; + } + $content_type = $real_content_type; + } + // ok, it's kosher, rewrite what we need + $uri->userinfo = null; + $uri->host = null; + $uri->port = null; + $uri->fragment = null; + $uri->query = null; + $uri->path = "$content_type;base64," . base64_encode($raw_data); + return true; + } + + /** + * @param int $errno + * @param string $errstr + */ + public function muteErrorHandler($errno, $errstr) + { + } +} diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php new file mode 100644 index 00000000000..215be4ba80f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php @@ -0,0 +1,44 @@ +userinfo = null; + // file:// makes no provisions for accessing the resource + $uri->port = null; + // While it seems to work on Firefox, the querystring has + // no possible effect and is thus stripped. + $uri->query = null; + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php new file mode 100644 index 00000000000..1eb43ee5c40 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php @@ -0,0 +1,58 @@ +query = null; + + // typecode check + $semicolon_pos = strrpos($uri->path, ';'); // reverse + if ($semicolon_pos !== false) { + $type = substr($uri->path, $semicolon_pos + 1); // no semicolon + $uri->path = substr($uri->path, 0, $semicolon_pos); + $type_ret = ''; + if (strpos($type, '=') !== false) { + // figure out whether or not the declaration is correct + list($key, $typecode) = explode('=', $type, 2); + if ($key !== 'type') { + // invalid key, tack it back on encoded + $uri->path .= '%3B' . $type; + } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') { + $type_ret = ";type=$typecode"; + } + } else { + $uri->path .= '%3B' . $type; + } + $uri->path = str_replace(';', '%3B', $uri->path); + $uri->path .= $type_ret; + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php new file mode 100644 index 00000000000..ce69ec438a2 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php @@ -0,0 +1,36 @@ +userinfo = null; + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php new file mode 100644 index 00000000000..0e96882db96 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php @@ -0,0 +1,18 @@ +userinfo = null; + $uri->host = null; + $uri->port = null; + // we need to validate path against RFC 2368's addr-spec + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php new file mode 100644 index 00000000000..7490927d63f --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php @@ -0,0 +1,35 @@ +userinfo = null; + $uri->host = null; + $uri->port = null; + $uri->query = null; + // typecode check needed on path + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php new file mode 100644 index 00000000000..f211d715e9d --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php @@ -0,0 +1,32 @@ +userinfo = null; + $uri->query = null; + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php new file mode 100644 index 00000000000..8cd1933527b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php @@ -0,0 +1,46 @@ +userinfo = null; + $uri->host = null; + $uri->port = null; + + // Delete all non-numeric characters, non-x characters + // from phone number, EXCEPT for a leading plus sign. + $uri->path = preg_replace('/(?!^\+)[^\dx]/', '', + // Normalize e(x)tension to lower-case + str_replace('X', 'x', $uri->path)); + + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php new file mode 100644 index 00000000000..4ac8a0b76e8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php @@ -0,0 +1,81 @@ +get('URI.AllowedSchemes'); + if (!$config->get('URI.OverrideAllowedSchemes') && + !isset($allowed_schemes[$scheme]) + ) { + return; + } + + if (isset($this->schemes[$scheme])) { + return $this->schemes[$scheme]; + } + if (!isset($allowed_schemes[$scheme])) { + return; + } + + $class = 'HTMLPurifier_URIScheme_' . $scheme; + if (!class_exists($class)) { + return; + } + $this->schemes[$scheme] = new $class(); + return $this->schemes[$scheme]; + } + + /** + * Registers a custom scheme to the cache, bypassing reflection. + * @param string $scheme Scheme name + * @param HTMLPurifier_URIScheme $scheme_obj + */ + public function register($scheme, $scheme_obj) + { + $this->schemes[$scheme] = $scheme_obj; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php new file mode 100644 index 00000000000..166f3bf306b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php @@ -0,0 +1,307 @@ + array( + 'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary + 'pt' => 4, + 'pc' => 48, + 'in' => 288, + self::METRIC => array('pt', '0.352777778', 'mm'), + ), + self::METRIC => array( + 'mm' => 1, + 'cm' => 10, + self::ENGLISH => array('mm', '2.83464567', 'pt'), + ), + ); + + /** + * Minimum bcmath precision for output. + * @type int + */ + protected $outputPrecision; + + /** + * Bcmath precision for internal calculations. + * @type int + */ + protected $internalPrecision; + + /** + * Whether or not BCMath is available. + * @type bool + */ + private $bcmath; + + public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) + { + $this->outputPrecision = $output_precision; + $this->internalPrecision = $internal_precision; + $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); + } + + /** + * Converts a length object of one unit into another unit. + * @param HTMLPurifier_Length $length + * Instance of HTMLPurifier_Length to convert. You must validate() + * it before passing it here! + * @param string $to_unit + * Unit to convert to. + * @return HTMLPurifier_Length|bool + * @note + * About precision: This conversion function pays very special + * attention to the incoming precision of values and attempts + * to maintain a number of significant figure. Results are + * fairly accurate up to nine digits. Some caveats: + * - If a number is zero-padded as a result of this significant + * figure tracking, the zeroes will be eliminated. + * - If a number contains less than four sigfigs ($outputPrecision) + * and this causes some decimals to be excluded, those + * decimals will be added on. + */ + public function convert($length, $to_unit) + { + if (!$length->isValid()) { + return false; + } + + $n = $length->getN(); + $unit = $length->getUnit(); + + if ($n === '0' || $unit === false) { + return new HTMLPurifier_Length('0', false); + } + + $state = $dest_state = false; + foreach (self::$units as $k => $x) { + if (isset($x[$unit])) { + $state = $k; + } + if (isset($x[$to_unit])) { + $dest_state = $k; + } + } + if (!$state || !$dest_state) { + return false; + } + + // Some calculations about the initial precision of the number; + // this will be useful when we need to do final rounding. + $sigfigs = $this->getSigFigs($n); + if ($sigfigs < $this->outputPrecision) { + $sigfigs = $this->outputPrecision; + } + + // BCMath's internal precision deals only with decimals. Use + // our default if the initial number has no decimals, or increase + // it by how ever many decimals, thus, the number of guard digits + // will always be greater than or equal to internalPrecision. + $log = (int)floor(log(abs($n), 10)); + $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision + + for ($i = 0; $i < 2; $i++) { + + // Determine what unit IN THIS SYSTEM we need to convert to + if ($dest_state === $state) { + // Simple conversion + $dest_unit = $to_unit; + } else { + // Convert to the smallest unit, pending a system shift + $dest_unit = self::$units[$state][$dest_state][0]; + } + + // Do the conversion if necessary + if ($dest_unit !== $unit) { + $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp); + $n = $this->mul($n, $factor, $cp); + $unit = $dest_unit; + } + + // Output was zero, so bail out early. Shouldn't ever happen. + if ($n === '') { + $n = '0'; + $unit = $to_unit; + break; + } + + // It was a simple conversion, so bail out + if ($dest_state === $state) { + break; + } + + if ($i !== 0) { + // Conversion failed! Apparently, the system we forwarded + // to didn't have this unit. This should never happen! + return false; + } + + // Pre-condition: $i == 0 + + // Perform conversion to next system of units + $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp); + $unit = self::$units[$state][$dest_state][2]; + $state = $dest_state; + + // One more loop around to convert the unit in the new system. + + } + + // Post-condition: $unit == $to_unit + if ($unit !== $to_unit) { + return false; + } + + // Useful for debugging: + //echo "
      n";
      +        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n
      \n"; + + $n = $this->round($n, $sigfigs); + if (strpos($n, '.') !== false) { + $n = rtrim($n, '0'); + } + $n = rtrim($n, '.'); + + return new HTMLPurifier_Length($n, $unit); + } + + /** + * Returns the number of significant figures in a string number. + * @param string $n Decimal number + * @return int number of sigfigs + */ + public function getSigFigs($n) + { + $n = ltrim($n, '0+-'); + $dp = strpos($n, '.'); // decimal position + if ($dp === false) { + $sigfigs = strlen(rtrim($n, '0')); + } else { + $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character + if ($dp !== 0) { + $sigfigs--; + } + } + return $sigfigs; + } + + /** + * Adds two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string + */ + private function add($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcadd($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 + (float)$s2, $scale); + } + } + + /** + * Multiples two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string + */ + private function mul($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcmul($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 * (float)$s2, $scale); + } + } + + /** + * Divides two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string + */ + private function div($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcdiv($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 / (float)$s2, $scale); + } + } + + /** + * Rounds a number according to the number of sigfigs it should have, + * using arbitrary precision when available. + * @param float $n + * @param int $sigfigs + * @return string + */ + private function round($n, $sigfigs) + { + $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1 + $rp = $sigfigs - $new_log - 1; // Number of decimal places needed + $neg = $n < 0 ? '-' : ''; // Negative sign + if ($this->bcmath) { + if ($rp >= 0) { + $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); + $n = bcdiv($n, '1', $rp); + } else { + // This algorithm partially depends on the standardized + // form of numbers that comes out of bcmath. + $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0); + $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1); + } + return $n; + } else { + return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1); + } + } + + /** + * Scales a float to $scale digits right of decimal point, like BCMath. + * @param float $r + * @param int $scale + * @return string + */ + private function scale($r, $scale) + { + if ($scale < 0) { + // The f sprintf type doesn't support negative numbers, so we + // need to cludge things manually. First get the string. + $r = sprintf('%.0f', (float)$r); + // Due to floating point precision loss, $r will more than likely + // look something like 4652999999999.9234. We grab one more digit + // than we need to precise from $r and then use that to round + // appropriately. + $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1); + // Now we return it, truncating the zero that was rounded off. + return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); + } + return sprintf('%.' . $scale . 'f', (float)$r); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php new file mode 100644 index 00000000000..0c97c8289d9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php @@ -0,0 +1,198 @@ + self::C_STRING, + 'istring' => self::ISTRING, + 'text' => self::TEXT, + 'itext' => self::ITEXT, + 'int' => self::C_INT, + 'float' => self::C_FLOAT, + 'bool' => self::C_BOOL, + 'lookup' => self::LOOKUP, + 'list' => self::ALIST, + 'hash' => self::HASH, + 'mixed' => self::C_MIXED + ); + + /** + * Lookup table of types that are string, and can have aliases or + * allowed value lists. + */ + public static $stringTypes = array( + self::C_STRING => true, + self::ISTRING => true, + self::TEXT => true, + self::ITEXT => true, + ); + + /** + * Validate a variable according to type. + * It may return NULL as a valid type if $allow_null is true. + * + * @param mixed $var Variable to validate + * @param int $type Type of variable, see HTMLPurifier_VarParser->types + * @param bool $allow_null Whether or not to permit null as a value + * @return string Validated and type-coerced variable + * @throws HTMLPurifier_VarParserException + */ + final public function parse($var, $type, $allow_null = false) + { + if (is_string($type)) { + if (!isset(HTMLPurifier_VarParser::$types[$type])) { + throw new HTMLPurifier_VarParserException("Invalid type '$type'"); + } else { + $type = HTMLPurifier_VarParser::$types[$type]; + } + } + $var = $this->parseImplementation($var, $type, $allow_null); + if ($allow_null && $var === null) { + return null; + } + // These are basic checks, to make sure nothing horribly wrong + // happened in our implementations. + switch ($type) { + case (self::C_STRING): + case (self::ISTRING): + case (self::TEXT): + case (self::ITEXT): + if (!is_string($var)) { + break; + } + if ($type == self::ISTRING || $type == self::ITEXT) { + $var = strtolower($var); + } + return $var; + case (self::C_INT): + if (!is_int($var)) { + break; + } + return $var; + case (self::C_FLOAT): + if (!is_float($var)) { + break; + } + return $var; + case (self::C_BOOL): + if (!is_bool($var)) { + break; + } + return $var; + case (self::LOOKUP): + case (self::ALIST): + case (self::HASH): + if (!is_array($var)) { + break; + } + if ($type === self::LOOKUP) { + foreach ($var as $k) { + if ($k !== true) { + $this->error('Lookup table contains value other than true'); + } + } + } elseif ($type === self::ALIST) { + $keys = array_keys($var); + if (array_keys($keys) !== $keys) { + $this->error('Indices for list are not uniform'); + } + } + return $var; + case (self::C_MIXED): + return $var; + default: + $this->errorInconsistent(get_class($this), $type); + } + $this->errorGeneric($var, $type); + } + + /** + * Actually implements the parsing. Base implementation does not + * do anything to $var. Subclasses should overload this! + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return string + */ + protected function parseImplementation($var, $type, $allow_null) + { + return $var; + } + + /** + * Throws an exception. + * @throws HTMLPurifier_VarParserException + */ + protected function error($msg) + { + throw new HTMLPurifier_VarParserException($msg); + } + + /** + * Throws an inconsistency exception. + * @note This should not ever be called. It would be called if we + * extend the allowed values of HTMLPurifier_VarParser without + * updating subclasses. + * @param string $class + * @param int $type + * @throws HTMLPurifier_Exception + */ + protected function errorInconsistent($class, $type) + { + throw new HTMLPurifier_Exception( + "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) . + " not implemented" + ); + } + + /** + * Generic error for if a type didn't work. + * @param mixed $var + * @param int $type + */ + protected function errorGeneric($var, $type) + { + $vtype = gettype($var); + $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype"); + } + + /** + * @param int $type + * @return string + */ + public static function getTypeName($type) + { + static $lookup; + if (!$lookup) { + // Lazy load the alternative lookup table + $lookup = array_flip(HTMLPurifier_VarParser::$types); + } + if (!isset($lookup[$type])) { + return 'unknown'; + } + return $lookup[$type]; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php new file mode 100644 index 00000000000..3bfbe838686 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php @@ -0,0 +1,130 @@ + $j) { + $var[$i] = trim($j); + } + if ($type === self::HASH) { + // key:value,key2:value2 + $nvar = array(); + foreach ($var as $keypair) { + $c = explode(':', $keypair, 2); + if (!isset($c[1])) { + continue; + } + $nvar[trim($c[0])] = trim($c[1]); + } + $var = $nvar; + } + } + if (!is_array($var)) { + break; + } + $keys = array_keys($var); + if ($keys === array_keys($keys)) { + if ($type == self::ALIST) { + return $var; + } elseif ($type == self::LOOKUP) { + $new = array(); + foreach ($var as $key) { + $new[$key] = true; + } + return $new; + } else { + break; + } + } + if ($type === self::ALIST) { + trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); + return array_values($var); + } + if ($type === self::LOOKUP) { + foreach ($var as $key => $value) { + if ($value !== true) { + trigger_error( + "Lookup array has non-true value at key '$key'; " . + "maybe your input array was not indexed numerically", + E_USER_WARNING + ); + } + $var[$key] = true; + } + } + return $var; + default: + $this->errorInconsistent(__CLASS__, $type); + } + $this->errorGeneric($var, $type); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php new file mode 100644 index 00000000000..f11c318efb6 --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php @@ -0,0 +1,38 @@ +evalExpression($var); + } + + /** + * @param string $expr + * @return mixed + * @throws HTMLPurifier_VarParserException + */ + protected function evalExpression($expr) + { + $var = null; + $result = eval("\$var = $expr;"); + if ($result === false) { + throw new HTMLPurifier_VarParserException("Fatal error in evaluated code"); + } + return $var; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php new file mode 100644 index 00000000000..5df3414959b --- /dev/null +++ b/lib/phpspreadsheet/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php @@ -0,0 +1,11 @@ +front = $front; + $this->back = $back; + } + + /** + * Creates a zipper from an array, with a hole in the + * 0-index position. + * @param Array to zipper-ify. + * @return Tuple of zipper and element of first position. + */ + static public function fromArray($array) { + $z = new self(array(), array_reverse($array)); + $t = $z->delete(); // delete the "dummy hole" + return array($z, $t); + } + + /** + * Convert zipper back into a normal array, optionally filling in + * the hole with a value. (Usually you should supply a $t, unless you + * are at the end of the array.) + */ + public function toArray($t = NULL) { + $a = $this->front; + if ($t !== NULL) $a[] = $t; + for ($i = count($this->back)-1; $i >= 0; $i--) { + $a[] = $this->back[$i]; + } + return $a; + } + + /** + * Move hole to the next element. + * @param $t Element to fill hole with + * @return Original contents of new hole. + */ + public function next($t) { + if ($t !== NULL) array_push($this->front, $t); + return empty($this->back) ? NULL : array_pop($this->back); + } + + /** + * Iterated hole advancement. + * @param $t Element to fill hole with + * @param $i How many forward to advance hole + * @return Original contents of new hole, i away + */ + public function advance($t, $n) { + for ($i = 0; $i < $n; $i++) { + $t = $this->next($t); + } + return $t; + } + + /** + * Move hole to the previous element + * @param $t Element to fill hole with + * @return Original contents of new hole. + */ + public function prev($t) { + if ($t !== NULL) array_push($this->back, $t); + return empty($this->front) ? NULL : array_pop($this->front); + } + + /** + * Delete contents of current hole, shifting hole to + * next element. + * @return Original contents of new hole. + */ + public function delete() { + return empty($this->back) ? NULL : array_pop($this->back); + } + + /** + * Returns true if we are at the end of the list. + * @return bool + */ + public function done() { + return empty($this->back); + } + + /** + * Insert element before hole. + * @param Element to insert + */ + public function insertBefore($t) { + if ($t !== NULL) array_push($this->front, $t); + } + + /** + * Insert element after hole. + * @param Element to insert + */ + public function insertAfter($t) { + if ($t !== NULL) array_push($this->back, $t); + } + + /** + * Splice in multiple elements at hole. Functional specification + * in terms of array_splice: + * + * $arr1 = $arr; + * $old1 = array_splice($arr1, $i, $delete, $replacement); + * + * list($z, $t) = HTMLPurifier_Zipper::fromArray($arr); + * $t = $z->advance($t, $i); + * list($old2, $t) = $z->splice($t, $delete, $replacement); + * $arr2 = $z->toArray($t); + * + * assert($old1 === $old2); + * assert($arr1 === $arr2); + * + * NB: the absolute index location after this operation is + * *unchanged!* + * + * @param Current contents of hole. + */ + public function splice($t, $delete, $replacement) { + // delete + $old = array(); + $r = $t; + for ($i = $delete; $i > 0; $i--) { + $old[] = $r; + $r = $this->delete(); + } + // insert + for ($i = count($replacement)-1; $i >= 0; $i--) { + $this->insertAfter($r); + $r = $replacement[$i]; + } + return array($old, $r); + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/FUNDING.yml b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/FUNDING.yml new file mode 100644 index 00000000000..d807a3f33f9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: zipstream diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..3c2e66d6f5c --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,12 @@ +# Description of the problem + +Please be very descriptive and include as much details as possible. + +# Example code + +# Informations + +* ZipStream-PHP version: +* PHP version: + +Please include any supplemental information you deem relevant to this issue. diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.gitignore b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.gitignore new file mode 100644 index 00000000000..5210738a9c9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.gitignore @@ -0,0 +1,6 @@ +clover.xml +composer.lock +coverage.clover +.idea +phpunit.xml +vendor diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.travis.yml b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.travis.yml new file mode 100644 index 00000000000..12c8a640e11 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/.travis.yml @@ -0,0 +1,12 @@ +language: php +dist: trusty +sudo: false +php: + - 7.1 + - 7.2 + - 7.3 +install: composer install +script: ./vendor/bin/phpunit --coverage-clover=coverage.clover +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CHANGELOG.md b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CHANGELOG.md new file mode 100644 index 00000000000..b1979abcc89 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CHANGELOG.md @@ -0,0 +1,51 @@ +# CHANGELOG for ZipStream-PHP + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [2.1.0] - 2020-06-01 +### Changed +- Don't execute ob_flush() when output buffering is not enabled (#152) +- Fix inconsistent return type on 32-bit systems (#149) Fix #144 +- Use mbstring polyfill (#151) +- Promote 7zip usage over unzip to avoid UTF-8 issues (#147) + +## [2.0.0] - 2020-02-22 +### Breaking change +- Only the self opened streams will be closed (#139) +If you were relying on ZipStream to close streams that the library didn't open, +you'll need to close them yourself now. + +### Changed +- Minor change to data descriptor (#136) + +## [1.2.0] - 2019-07-11 + +### Added +- Option to flush output buffer after every write (#122) + +## [1.1.0] - 2019-04-30 + +### Fixed +- Honor last-modified timestamps set via `ZipStream\Option\File::setTime()` (#106) +- Documentation regarding output of HTTP headers +- Test warnings with PHPUnit (#109) + +### Added +- Test for FileNotReadableException (#114) +- Size attribute to File options (#113) +- Tests on PHP 7.3 (#108) + +## [1.0.0] - 2019-04-17 + +### Breaking changes +- Mininum PHP version is now 7.1 +- Options are now passed to the ZipStream object via the Option\Archive object. See the wiki for available options and code examples + +### Added +- Add large file support with Zip64 headers + +### Changed +- Major refactoring and code cleanup diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CONTRIBUTING.md b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CONTRIBUTING.md new file mode 100644 index 00000000000..f8ef5a57318 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# ZipStream Readme for Contributors +## Code styling +### Indention +For spaces are used to indent code. The convention is [K&R](http://en.wikipedia.org/wiki/Indent_style#K&R) + +### Comments +Double Slashes are used for an one line comment. + +Classes, Variables, Methods etc: + +```php +/** + * My comment + * + * @myanotation like @param etc. + */ +``` + +## Pull requests +Feel free to submit pull requests. + +## Testing +For every new feature please write a new PHPUnit test. + +Before every commit execute `./vendor/bin/phpunit` to check if your changes wrecked something: diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/LICENSE b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/LICENSE new file mode 100644 index 00000000000..ebe7fe2f8a1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/LICENSE @@ -0,0 +1,24 @@ +MIT License + +Copyright (C) 2007-2009 Paul Duncan +Copyright (C) 2014 Jonatan Männchen +Copyright (C) 2014 Jesse G. Donat +Copyright (C) 2018 Nicolas CARPi + +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/lib/phpspreadsheet/vendor/maennchen/zipstream-php/README.md b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/README.md new file mode 100644 index 00000000000..c2e832b6fbe --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/README.md @@ -0,0 +1,123 @@ +# ZipStream-PHP + +[![Build Status](https://travis-ci.org/maennchen/ZipStream-PHP.svg?branch=master)](https://travis-ci.org/maennchen/ZipStream-PHP) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/) +[![Code Coverage](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/) +[![Latest Stable Version](https://poser.pugx.org/maennchen/zipstream-php/v/stable)](https://packagist.org/packages/maennchen/zipstream-php) +[![Total Downloads](https://poser.pugx.org/maennchen/zipstream-php/downloads)](https://packagist.org/packages/maennchen/zipstream-php) +[![Financial Contributors on Open Collective](https://opencollective.com/zipstream/all/badge.svg?label=financial+contributors)](https://opencollective.com/zipstream) [![License](https://img.shields.io/github/license/maennchen/zipstream-php.svg)](LICENSE) + +## Overview + +A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream. + +Please see the [LICENSE](LICENSE) file for licensing and warranty information. + +## Installation + +Simply add a dependency on maennchen/zipstream-php to your project's composer.json file if you use Composer to manage the dependencies of your project. Use following command to add the package to your project's dependencies: + +```bash +composer require maennchen/zipstream-php +``` + +## Usage and options + +Here's a simple example: + +```php +// Autoload the dependencies +require 'vendor/autoload.php'; + +// enable output of HTTP headers +$options = new ZipStream\Option\Archive(); +$options->setSendHttpHeaders(true); + +// create a new zipstream object +$zip = new ZipStream\ZipStream('example.zip', $options); + +// create a file named 'hello.txt' +$zip->addFile('hello.txt', 'This is the contents of hello.txt'); + +// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg' +$zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg'); + +// add a file named 'goodbye.txt' from an open stream resource +$fp = tmpfile(); +fwrite($fp, 'The quick brown fox jumped over the lazy dog.'); +rewind($fp); +$zip->addFileFromStream('goodbye.txt', $fp); +fclose($fp); + +// finish the zip stream +$zip->finish(); +``` + +You can also add comments, modify file timestamps, and customize (or +disable) the HTTP headers. It is also possible to specify the storage method when adding files, +the current default storage method is 'deflate' i.e files are stored with Compression mode 0x08. + +See the [Wiki](https://github.com/maennchen/ZipStream-PHP/wiki) for details. + +## Known issue + +The native Mac OS archive extraction tool might not open archives in some conditions. A workaround is to disable the Zip64 feature with the option `$opt->setEnableZip64(false)`. This limits the archive to 4 Gb and 64k files but will allow Mac OS users to open them without issue. See #116. + +The linux `unzip` utility might not handle properly unicode characters. It is recommended to extract with another tool like [7-zip](https://www.7-zip.org/). See #146. + +## Upgrade to version 2.0.0 + +* Only the self opened streams will be closed (#139) +If you were relying on ZipStream to close streams that the library didn't open, +you'll need to close them yourself now. + +## Upgrade to version 1.0.0 + +* All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples. +* The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1. + +## Usage with Symfony and S3 + +You can find example code on [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Symfony-example). + +## Contributing + +ZipStream-PHP is a collaborative project. Please take a look at the [CONTRIBUTING.md](CONTRIBUTING.md) file. + +## About the Authors + +* Paul Duncan - https://pablotron.org/ +* Jonatan Männchen - https://maennchen.dev +* Jesse G. Donat - https://donatstudios.com +* Nicolas CARPi - https://www.deltablot.com +* Nik Barham - https://www.brokencube.co.uk + +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. +
      + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/zipstream/contribute)] + +#### Individuals + + + +#### Organizations + +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/zipstream/contribute)] + + + + + + + + + + + diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/composer.json b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/composer.json new file mode 100644 index 00000000000..103c78c7056 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/composer.json @@ -0,0 +1,41 @@ +{ + "name": "maennchen/zipstream-php", + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": ["zip", "stream"], + "type": "library", + "license": "MIT", + "authors": [{ + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "require": { + "php": ">= 7.1", + "symfony/polyfill-mbstring": "^1.0", + "psr/http-message": "^1.0", + "myclabs/php-enum": "^1.5" + }, + "require-dev": { + "phpunit/phpunit": ">= 7.5", + "guzzlehttp/guzzle": ">= 6.3", + "ext-zip": "*", + "mikey179/vfsstream": "^1.6" + }, + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/phpunit.xml.dist b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/phpunit.xml.dist new file mode 100644 index 00000000000..f6e722796ee --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + test + + + + + + + + + + src + + + diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/psalm.xml b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/psalm.xml new file mode 100644 index 00000000000..42f355b2cec --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/psalm.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Bigint.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Bigint.php new file mode 100644 index 00000000000..52ccfd2ea48 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Bigint.php @@ -0,0 +1,172 @@ +fillBytes($value, 0, 8); + } + + /** + * Fill the bytes field with int + * + * @param int $value + * @param int $start + * @param int $count + * @return void + */ + protected function fillBytes(int $value, int $start, int $count): void + { + for ($i = 0; $i < $count; $i++) { + $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF; + $value >>= 8; + } + } + + /** + * Get an instance + * + * @param int $value + * @return Bigint + */ + public static function init(int $value = 0): self + { + return new self($value); + } + + /** + * Fill bytes from low to high + * + * @param int $low + * @param int $high + * @return Bigint + */ + public static function fromLowHigh(int $low, int $high): self + { + $bigint = new Bigint(); + $bigint->fillBytes($low, 0, 4); + $bigint->fillBytes($high, 4, 4); + return $bigint; + } + + /** + * Get high 32 + * + * @return int + */ + public function getHigh32(): int + { + return $this->getValue(4, 4); + } + + /** + * Get value from bytes array + * + * @param int $end + * @param int $length + * @return int + */ + public function getValue(int $end = 0, int $length = 8): int + { + $result = 0; + for ($i = $end + $length - 1; $i >= $end; $i--) { + $result <<= 8; + $result |= $this->bytes[$i]; + } + return $result; + } + + /** + * Get low FF + * + * @param bool $force + * @return float + */ + public function getLowFF(bool $force = false): float + { + if ($force || $this->isOver32()) { + return (float)0xFFFFFFFF; + } + return (float)$this->getLow32(); + } + + /** + * Check if is over 32 + * + * @param bool $force + * @return bool + */ + public function isOver32(bool $force = false): bool + { + // value 0xFFFFFFFF already needs a Zip64 header + return $force || + max(array_slice($this->bytes, 4, 4)) > 0 || + min(array_slice($this->bytes, 0, 4)) === 0xFF; + } + + /** + * Get low 32 + * + * @return int + */ + public function getLow32(): int + { + return $this->getValue(0, 4); + } + + /** + * Get hexadecimal + * + * @return string + */ + public function getHex64(): string + { + $result = '0x'; + for ($i = 7; $i >= 0; $i--) { + $result .= sprintf('%02X', $this->bytes[$i]); + } + return $result; + } + + /** + * Add + * + * @param Bigint $other + * @return Bigint + */ + public function add(Bigint $other): Bigint + { + $result = clone $this; + $overflow = false; + for ($i = 0; $i < 8; $i++) { + $result->bytes[$i] += $other->bytes[$i]; + if ($overflow) { + $result->bytes[$i]++; + $overflow = false; + } + if ($result->bytes[$i] & 0x100) { + $overflow = true; + $result->bytes[$i] &= 0xFF; + } + } + if ($overflow) { + throw new OverflowException; + } + return $result; + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/DeflateStream.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/DeflateStream.php new file mode 100644 index 00000000000..d6c272885b7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/DeflateStream.php @@ -0,0 +1,70 @@ +filter) { + $this->removeDeflateFilter(); + $this->seek(0); + $this->addDeflateFilter($this->options); + } else { + rewind($this->stream); + } + } + + /** + * Remove the deflate filter + * + * @return void + */ + public function removeDeflateFilter(): void + { + if (!$this->filter) { + return; + } + stream_filter_remove($this->filter); + $this->filter = null; + } + + /** + * Add a deflate filter + * + * @param Option\File $options + * @return void + */ + public function addDeflateFilter(Option\File $options): void + { + $this->options = $options; + // parameter 4 for stream_filter_append expects array + // so we convert the option object in an array + $optionsArr = [ + 'comment' => $options->getComment(), + 'method' => $options->getMethod(), + 'deflateLevel' => $options->getDeflateLevel(), + 'time' => $options->getTime() + ]; + $this->filter = stream_filter_append( + $this->stream, + 'zlib.deflate', + STREAM_FILTER_READ, + $optionsArr + ); + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception.php new file mode 100644 index 00000000000..18ccfbbdc1f --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Exception.php @@ -0,0 +1,11 @@ +zip = $zip; + + $this->name = $name; + $this->opt = $opt ?: new FileOptions(); + $this->method = $this->opt->getMethod(); + $this->version = Version::STORE(); + $this->ofs = new Bigint(); + } + + public function processPath(string $path): void + { + if (!is_readable($path)) { + if (!file_exists($path)) { + throw new FileNotFoundException($path); + } + throw new FileNotReadableException($path); + } + if ($this->zip->isLargeFile($path) === false) { + $data = file_get_contents($path); + $this->processData($data); + } else { + $this->method = $this->zip->opt->getLargeFileMethod(); + + $stream = new DeflateStream(fopen($path, 'rb')); + $this->processStream($stream); + $stream->close(); + } + } + + public function processData(string $data): void + { + $this->len = new Bigint(strlen($data)); + $this->crc = crc32($data); + + // compress data if needed + if ($this->method->equals(Method::DEFLATE())) { + $data = gzdeflate($data); + } + + $this->zlen = new Bigint(strlen($data)); + $this->addFileHeader(); + $this->zip->send($data); + $this->addFileFooter(); + } + + /** + * Create and send zip header for this file. + * + * @return void + * @throws \ZipStream\Exception\EncodingException + */ + public function addFileHeader(): void + { + $name = static::filterFilename($this->name); + + // calculate name length + $nameLength = strlen($name); + + // create dos timestamp + $time = static::dosTime($this->opt->getTime()->getTimestamp()); + + $comment = $this->opt->getComment(); + + if (!mb_check_encoding($name, 'ASCII') || + !mb_check_encoding($comment, 'ASCII')) { + // Sets Bit 11: Language encoding flag (EFS). If this bit is set, + // the filename and comment fields for this file + // MUST be encoded using UTF-8. (see APPENDIX D) + if (!mb_check_encoding($name, 'UTF-8') || + !mb_check_encoding($comment, 'UTF-8')) { + throw new EncodingException( + 'File name and comment should use UTF-8 ' . + 'if one of them does not fit into ASCII range.' + ); + } + $this->bits |= self::BIT_EFS_UTF8; + } + + if ($this->method->equals(Method::DEFLATE())) { + $this->version = Version::DEFLATE(); + } + + $force = (boolean)($this->bits & self::BIT_ZERO_HEADER) && + $this->zip->opt->isEnableZip64(); + + $footer = $this->buildZip64ExtraBlock($force); + + // If this file will start over 4GB limit in ZIP file, + // CDR record will have to use Zip64 extension to describe offset + // to keep consistency we use the same value here + if ($this->zip->ofs->isOver32()) { + $this->version = Version::ZIP64(); + } + + $fields = [ + ['V', ZipStream::FILE_HEADER_SIGNATURE], + ['v', $this->version->getValue()], // Version needed to Extract + ['v', $this->bits], // General purpose bit flags - data descriptor flag set + ['v', $this->method->getValue()], // Compression method + ['V', $time], // Timestamp (DOS Format) + ['V', $this->crc], // CRC32 of data (0 -> moved to data descriptor footer) + ['V', $this->zlen->getLowFF($force)], // Length of compressed data (forced to 0xFFFFFFFF for zero header) + ['V', $this->len->getLowFF($force)], // Length of original data (forced to 0xFFFFFFFF for zero header) + ['v', $nameLength], // Length of filename + ['v', strlen($footer)], // Extra data (see above) + ]; + + // pack fields and calculate "total" length + $header = ZipStream::packFields($fields); + + // print header and filename + $data = $header . $name . $footer; + $this->zip->send($data); + + // save header length + $this->hlen = Bigint::init(strlen($data)); + } + + /** + * Strip characters that are not legal in Windows filenames + * to prevent compatibility issues + * + * @param string $filename Unprocessed filename + * @return string + */ + public static function filterFilename(string $filename): string + { + // strip leading slashes from file name + // (fixes bug in windows archive viewer) + $filename = preg_replace('/^\\/+/', '', $filename); + + return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename); + } + + /** + * Convert a UNIX timestamp to a DOS timestamp. + * + * @param int $when + * @return int DOS Timestamp + */ + final protected static function dosTime(int $when): int + { + // get date array for timestamp + $d = getdate($when); + + // set lower-bound on dates + if ($d['year'] < 1980) { + $d = array( + 'year' => 1980, + 'mon' => 1, + 'mday' => 1, + 'hours' => 0, + 'minutes' => 0, + 'seconds' => 0 + ); + } + + // remove extra years from 1980 + $d['year'] -= 1980; + + // return date string + return + ($d['year'] << 25) | + ($d['mon'] << 21) | + ($d['mday'] << 16) | + ($d['hours'] << 11) | + ($d['minutes'] << 5) | + ($d['seconds'] >> 1); + } + + protected function buildZip64ExtraBlock(bool $force = false): string + { + + $fields = []; + if ($this->len->isOver32($force)) { + $fields[] = ['P', $this->len]; // Length of original data + } + + if ($this->len->isOver32($force)) { + $fields[] = ['P', $this->zlen]; // Length of compressed data + } + + if ($this->ofs->isOver32()) { + $fields[] = ['P', $this->ofs]; // Offset of local header record + } + + if (!empty($fields)) { + if (!$this->zip->opt->isEnableZip64()) { + throw new OverflowException(); + } + + array_unshift( + $fields, + ['v', 0x0001], // 64 bit extension + ['v', count($fields) * 8] // Length of data block + ); + $this->version = Version::ZIP64(); + } + + return ZipStream::packFields($fields); + } + + /** + * Create and send data descriptor footer for this file. + * + * @return void + */ + + public function addFileFooter(): void + { + + if ($this->bits & self::BIT_ZERO_HEADER) { + // compressed and uncompressed size + $sizeFormat = 'V'; + if ($this->zip->opt->isEnableZip64()) { + $sizeFormat = 'P'; + } + $fields = [ + ['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE], + ['V', $this->crc], // CRC32 + [$sizeFormat, $this->zlen], // Length of compressed data + [$sizeFormat, $this->len], // Length of original data + ]; + + $footer = ZipStream::packFields($fields); + $this->zip->send($footer); + } else { + $footer = ''; + } + $this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer))); + $this->zip->addToCdr($this); + } + + public function processStream(StreamInterface $stream): void + { + $this->zlen = new Bigint(); + $this->len = new Bigint(); + + if ($this->zip->opt->isZeroHeader()) { + $this->processStreamWithZeroHeader($stream); + } else { + $this->processStreamWithComputedHeader($stream); + } + } + + protected function processStreamWithZeroHeader(StreamInterface $stream): void + { + $this->bits |= self::BIT_ZERO_HEADER; + $this->addFileHeader(); + $this->readStream($stream, self::COMPUTE | self::SEND); + $this->addFileFooter(); + } + + protected function readStream(StreamInterface $stream, ?int $options = null): void + { + $this->deflateInit(); + $total = 0; + $size = $this->opt->getSize(); + while (!$stream->eof() && ($size === 0 || $total < $size)) { + $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE); + $total += strlen($data); + if ($size > 0 && $total > $size) { + $data = substr($data, 0 , strlen($data)-($total - $size)); + } + $this->deflateData($stream, $data, $options); + if ($options & self::SEND) { + $this->zip->send($data); + } + } + $this->deflateFinish($options); + } + + protected function deflateInit(): void + { + $this->hash = hash_init(self::HASH_ALGORITHM); + if ($this->method->equals(Method::DEFLATE())) { + $this->deflate = deflate_init( + ZLIB_ENCODING_RAW, + ['level' => $this->opt->getDeflateLevel()] + ); + } + } + + protected function deflateData(StreamInterface $stream, string &$data, ?int $options = null): void + { + if ($options & self::COMPUTE) { + $this->len = $this->len->add(Bigint::init(strlen($data))); + hash_update($this->hash, $data); + } + if ($this->deflate) { + $data = deflate_add( + $this->deflate, + $data, + $stream->eof() + ? ZLIB_FINISH + : ZLIB_NO_FLUSH + ); + } + if ($options & self::COMPUTE) { + $this->zlen = $this->zlen->add(Bigint::init(strlen($data))); + } + } + + protected function deflateFinish(?int $options = null): void + { + if ($options & self::COMPUTE) { + $this->crc = hexdec(hash_final($this->hash)); + } + } + + protected function processStreamWithComputedHeader(StreamInterface $stream): void + { + $this->readStream($stream, self::COMPUTE); + $stream->rewind(); + + // incremental compression with deflate_add + // makes this second read unnecessary + // but it is only available from PHP 7.0 + if (!$this->deflate && $stream instanceof DeflateStream && $this->method->equals(Method::DEFLATE())) { + $stream->addDeflateFilter($this->opt); + $this->zlen = new Bigint(); + while (!$stream->eof()) { + $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE); + $this->zlen = $this->zlen->add(Bigint::init(strlen($data))); + } + $stream->rewind(); + } + + $this->addFileHeader(); + $this->readStream($stream, self::SEND); + $this->addFileFooter(); + } + + /** + * Send CDR record for specified file. + * + * @return string + */ + public function getCdrFile(): string + { + $name = static::filterFilename($this->name); + + // get attributes + $comment = $this->opt->getComment(); + + // get dos timestamp + $time = static::dosTime($this->opt->getTime()->getTimestamp()); + + $footer = $this->buildZip64ExtraBlock(); + + $fields = [ + ['V', ZipStream::CDR_FILE_SIGNATURE], // Central file header signature + ['v', ZipStream::ZIP_VERSION_MADE_BY], // Made by version + ['v', $this->version->getValue()], // Extract by version + ['v', $this->bits], // General purpose bit flags - data descriptor flag set + ['v', $this->method->getValue()], // Compression method + ['V', $time], // Timestamp (DOS Format) + ['V', $this->crc], // CRC32 + ['V', $this->zlen->getLowFF()], // Compressed Data Length + ['V', $this->len->getLowFF()], // Original Data Length + ['v', strlen($name)], // Length of filename + ['v', strlen($footer)], // Extra data len (see above) + ['v', strlen($comment)], // Length of comment + ['v', 0], // Disk number + ['v', 0], // Internal File Attributes + ['V', 32], // External File Attributes + ['V', $this->ofs->getLowFF()] // Relative offset of local header + ]; + + // pack fields, then append name and comment + $header = ZipStream::packFields($fields); + + return $header . $name . $footer . $comment; + } + + /** + * @return Bigint + */ + public function getTotalLength(): Bigint + { + return $this->totalLength; + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Archive.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Archive.php new file mode 100644 index 00000000000..b6b95cce54c --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Archive.php @@ -0,0 +1,261 @@ + 4 GB or file count > 64k) + * + * @var bool + */ + private $enableZip64 = true; + /** + * Enable streaming files with single read where + * general purpose bit 3 indicates local file header + * contain zero values in crc and size fields, + * these appear only after file contents + * in data descriptor block. + * + * @var bool + */ + private $zeroHeader = false; + /** + * Enable reading file stat for determining file size. + * When a 32-bit system reads file size that is + * over 2 GB, invalid value appears in file size + * due to integer overflow. Should be disabled on + * 32-bit systems with method addFileFromPath + * if any file may exceed 2 GB. In this case file + * will be read in blocks and correct size will be + * determined from content. + * + * @var bool + */ + private $statFiles = true; + /** + * Enable flush after every write to output stream. + * @var bool + */ + private $flushOutput = false; + /** + * HTTP Content-Disposition. Defaults to + * 'attachment', where + * FILENAME is the specified filename. + * + * Note that this does nothing if you are + * not sending HTTP headers. + * + * @var string + */ + private $contentDisposition = 'attachment'; + /** + * Note that this does nothing if you are + * not sending HTTP headers. + * + * @var string + */ + private $contentType = 'application/x-zip'; + /** + * @var int + */ + private $deflateLevel = 6; + + /** + * @var resource + */ + private $outputStream; + + /** + * Options constructor. + */ + public function __construct() + { + $this->largeFileMethod = Method::STORE(); + $this->outputStream = fopen('php://output', 'wb'); + } + + public function getComment(): string + { + return $this->comment; + } + + public function setComment(string $comment): void + { + $this->comment = $comment; + } + + public function getLargeFileSize(): int + { + return $this->largeFileSize; + } + + public function setLargeFileSize(int $largeFileSize): void + { + $this->largeFileSize = $largeFileSize; + } + + public function getLargeFileMethod(): Method + { + return $this->largeFileMethod; + } + + public function setLargeFileMethod(Method $largeFileMethod): void + { + $this->largeFileMethod = $largeFileMethod; + } + + public function isSendHttpHeaders(): bool + { + return $this->sendHttpHeaders; + } + + public function setSendHttpHeaders(bool $sendHttpHeaders): void + { + $this->sendHttpHeaders = $sendHttpHeaders; + } + + public function getHttpHeaderCallback(): Callable + { + return $this->httpHeaderCallback; + } + + public function setHttpHeaderCallback(Callable $httpHeaderCallback): void + { + $this->httpHeaderCallback = $httpHeaderCallback; + } + + public function isEnableZip64(): bool + { + return $this->enableZip64; + } + + public function setEnableZip64(bool $enableZip64): void + { + $this->enableZip64 = $enableZip64; + } + + public function isZeroHeader(): bool + { + return $this->zeroHeader; + } + + public function setZeroHeader(bool $zeroHeader): void + { + $this->zeroHeader = $zeroHeader; + } + + public function isFlushOutput(): bool + { + return $this->flushOutput; + } + + public function setFlushOutput(bool $flushOutput): void + { + $this->flushOutput = $flushOutput; + } + + public function isStatFiles(): bool + { + return $this->statFiles; + } + + public function setStatFiles(bool $statFiles): void + { + $this->statFiles = $statFiles; + } + + public function getContentDisposition(): string + { + return $this->contentDisposition; + } + + public function setContentDisposition(string $contentDisposition): void + { + $this->contentDisposition = $contentDisposition; + } + + public function getContentType(): string + { + return $this->contentType; + } + + public function setContentType(string $contentType): void + { + $this->contentType = $contentType; + } + + /** + * @return resource + */ + public function getOutputStream() + { + return $this->outputStream; + } + + /** + * @param resource $outputStream + */ + public function setOutputStream($outputStream): void + { + $this->outputStream = $outputStream; + } + + /** + * @return int + */ + public function getDeflateLevel(): int + { + return $this->deflateLevel; + } + + /** + * @param int $deflateLevel + */ + public function setDeflateLevel(int $deflateLevel): void + { + $this->deflateLevel = $deflateLevel; + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/File.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/File.php new file mode 100644 index 00000000000..7fd29ea55c5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/File.php @@ -0,0 +1,116 @@ +deflateLevel = $this->deflateLevel ?: $archiveOptions->getDeflateLevel(); + $this->time = $this->time ?: new DateTime(); + } + + /** + * @return string + */ + public function getComment(): string + { + return $this->comment; + } + + /** + * @param string $comment + */ + public function setComment(string $comment): void + { + $this->comment = $comment; + } + + /** + * @return Method + */ + public function getMethod(): Method + { + return $this->method ?: Method::DEFLATE(); + } + + /** + * @param Method $method + */ + public function setMethod(Method $method): void + { + $this->method = $method; + } + + /** + * @return int + */ + public function getDeflateLevel(): int + { + return $this->deflateLevel ?: Archive::DEFAULT_DEFLATE_LEVEL; + } + + /** + * @param int $deflateLevel + */ + public function setDeflateLevel(int $deflateLevel): void + { + $this->deflateLevel = $deflateLevel; + } + + /** + * @return DateTime + */ + public function getTime(): DateTime + { + return $this->time; + } + + /** + * @param DateTime $time + */ + public function setTime(DateTime $time): void + { + $this->time = $time; + } + + /** + * @return int + */ + public function getSize(): int + { + return $this->size; + } + + /** + * @param int $size + */ + public function setSize(int $size): void + { + $this->size = $size; + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Method.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Method.php new file mode 100644 index 00000000000..bbec84c00df --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/Option/Method.php @@ -0,0 +1,19 @@ +stream = $stream; + } + + /** + * Closes the stream and any underlying resources. + * + * @return void + */ + public function close(): void + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->detach(); + } + + /** + * Separates any underlying resources from the stream. + * + * After the stream has been detached, the stream is in an unusable state. + * + * @return resource|null Underlying PHP stream, if any + */ + public function detach() + { + $result = $this->stream; + $this->stream = null; + return $result; + } + + /** + * Reads all data from the stream into a string, from the beginning to end. + * + * This method MUST attempt to seek to the beginning of the stream before + * reading data and read the stream until the end is reached. + * + * Warning: This could attempt to load a large amount of data into memory. + * + * This method MUST NOT raise an exception in order to conform with PHP's + * string casting operations. + * + * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring + * @return string + */ + public function __toString(): string + { + try { + $this->seek(0); + } catch (\RuntimeException $e) {} + return (string) stream_get_contents($this->stream); + } + + /** + * Seek to a position in the stream. + * + * @link http://www.php.net/manual/en/function.fseek.php + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated + * based on the seek offset. Valid values are identical to the built-in + * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to + * offset bytes SEEK_CUR: Set position to current location plus offset + * SEEK_END: Set position to end-of-stream plus offset. + * @throws \RuntimeException on failure. + */ + public function seek($offset, $whence = SEEK_SET): void + { + if (!$this->isSeekable()) { + throw new RuntimeException; + } + if (fseek($this->stream, $offset, $whence) !== 0) { + throw new RuntimeException; + } + } + + /** + * Returns whether or not the stream is seekable. + * + * @return bool + */ + public function isSeekable(): bool + { + return (bool)$this->getMetadata('seekable'); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * The keys returned are identical to the keys returned from PHP's + * stream_get_meta_data() function. + * + * @link http://php.net/manual/en/function.stream-get-meta-data.php + * @param string $key Specific metadata to retrieve. + * @return array|mixed|null Returns an associative array if no key is + * provided. Returns a specific key value if a key is provided and the + * value is found, or null if the key is not found. + */ + public function getMetadata($key = null) + { + $metadata = stream_get_meta_data($this->stream); + return $key !== null ? @$metadata[$key] : $metadata; + } + + /** + * Get the size of the stream if known. + * + * @return int|null Returns the size in bytes if known, or null if unknown. + */ + public function getSize(): ?int + { + $stats = fstat($this->stream); + return $stats['size']; + } + + /** + * Returns the current position of the file read/write pointer + * + * @return int Position of the file pointer + * @throws \RuntimeException on error. + */ + public function tell(): int + { + $position = ftell($this->stream); + if ($position === false) { + throw new RuntimeException; + } + return $position; + } + + /** + * Returns true if the stream is at the end of the stream. + * + * @return bool + */ + public function eof(): bool + { + return feof($this->stream); + } + + /** + * Seek to the beginning of the stream. + * + * If the stream is not seekable, this method will raise an exception; + * otherwise, it will perform a seek(0). + * + * @see seek() + * @link http://www.php.net/manual/en/function.fseek.php + * @throws \RuntimeException on failure. + */ + public function rewind(): void + { + $this->seek(0); + } + + /** + * Write data to the stream. + * + * @param string $string The string that is to be written. + * @return int Returns the number of bytes written to the stream. + * @throws \RuntimeException on failure. + */ + public function write($string): int + { + if (!$this->isWritable()) { + throw new RuntimeException; + } + if (fwrite($this->stream, $string) === false) { + throw new RuntimeException; + } + return \mb_strlen($string); + } + + /** + * Returns whether or not the stream is writable. + * + * @return bool + */ + public function isWritable(): bool + { + return preg_match('/[waxc+]/', $this->getMetadata('mode')) === 1; + } + + /** + * Read data from the stream. + * + * @param int $length Read up to $length bytes from the object and return + * them. Fewer than $length bytes may be returned if underlying stream + * call returns fewer bytes. + * @return string Returns the data read from the stream, or an empty string + * if no bytes are available. + * @throws \RuntimeException if an error occurs. + */ + public function read($length): string + { + if (!$this->isReadable()) { + throw new RuntimeException; + } + $result = fread($this->stream, $length); + if ($result === false) { + throw new RuntimeException; + } + return $result; + } + + /** + * Returns whether or not the stream is readable. + * + * @return bool + */ + public function isReadable(): bool + { + return preg_match('/[r+]/', $this->getMetadata('mode')) === 1; + } + + /** + * Returns the remaining contents in a string + * + * @return string + * @throws \RuntimeException if unable to read or an error occurs while + * reading. + */ + public function getContents(): string + { + if (!$this->isReadable()) { + throw new RuntimeException; + } + $result = stream_get_contents($this->stream); + if ($result === false) { + throw new RuntimeException; + } + return $result; + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/ZipStream.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/ZipStream.php new file mode 100644 index 00000000000..e83038cf016 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/src/ZipStream.php @@ -0,0 +1,599 @@ +addFile('some_file.gif', $data); + * + * * add second file + * $data = file_get_contents('some_file.gif'); + * $zip->addFile('another_file.png', $data); + * + * 3. Finish the zip stream: + * + * $zip->finish(); + * + * You can also add an archive comment, add comments to individual files, + * and adjust the timestamp of files. See the API documentation for each + * method below for additional information. + * + * Example: + * + * // create a new zip stream object + * $zip = new ZipStream('some_files.zip'); + * + * // list of local files + * $files = array('foo.txt', 'bar.jpg'); + * + * // read and add each file to the archive + * foreach ($files as $path) + * $zip->addFile($path, file_get_contents($path)); + * + * // write archive footer to stream + * $zip->finish(); + */ +class ZipStream +{ + /** + * This number corresponds to the ZIP version/OS used (2 bytes) + * From: https://www.iana.org/assignments/media-types/application/zip + * The upper byte (leftmost one) indicates the host system (OS) for the + * file. Software can use this information to determine + * the line record format for text files etc. The current + * mappings are: + * + * 0 - MS-DOS and OS/2 (F.A.T. file systems) + * 1 - Amiga 2 - VAX/VMS + * 3 - *nix 4 - VM/CMS + * 5 - Atari ST 6 - OS/2 H.P.F.S. + * 7 - Macintosh 8 - Z-System + * 9 - CP/M 10 thru 255 - unused + * + * The lower byte (rightmost one) indicates the version number of the + * software used to encode the file. The value/10 + * indicates the major version number, and the value + * mod 10 is the minor version number. + * Here we are using 6 for the OS, indicating OS/2 H.P.F.S. + * to prevent file permissions issues upon extract (see #84) + * 0x603 is 00000110 00000011 in binary, so 6 and 3 + */ + const ZIP_VERSION_MADE_BY = 0x603; + + /** + * The following signatures end with 0x4b50, which in ASCII is PK, + * the initials of the inventor Phil Katz. + * See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers + */ + const FILE_HEADER_SIGNATURE = 0x04034b50; + const CDR_FILE_SIGNATURE = 0x02014b50; + const CDR_EOF_SIGNATURE = 0x06054b50; + const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50; + const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50; + const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50; + + /** + * Global Options + * + * @var ArchiveOptions + */ + public $opt; + + /** + * @var array + */ + public $files = []; + + /** + * @var Bigint + */ + public $cdr_ofs; + + /** + * @var Bigint + */ + public $ofs; + + /** + * @var bool + */ + protected $need_headers; + + /** + * @var null|String + */ + protected $output_name; + + /** + * Create a new ZipStream object. + * + * Parameters: + * + * @param String $name - Name of output file (optional). + * @param ArchiveOptions $opt - Archive Options + * + * Large File Support: + * + * By default, the method addFileFromPath() will send send files + * larger than 20 megabytes along raw rather than attempting to + * compress them. You can change both the maximum size and the + * compression behavior using the largeFile* options above, with the + * following caveats: + * + * * For "small" files (e.g. files smaller than largeFileSize), the + * memory use can be up to twice that of the actual file. In other + * words, adding a 10 megabyte file to the archive could potentially + * occupy 20 megabytes of memory. + * + * * Enabling compression on large files (e.g. files larger than + * large_file_size) is extremely slow, because ZipStream has to pass + * over the large file once to calculate header information, and then + * again to compress and send the actual data. + * + * Examples: + * + * // create a new zip file named 'foo.zip' + * $zip = new ZipStream('foo.zip'); + * + * // create a new zip file named 'bar.zip' with a comment + * $opt->setComment = 'this is a comment for the zip file.'; + * $zip = new ZipStream('bar.zip', $opt); + * + * Notes: + * + * In order to let this library send HTTP headers, a filename must be given + * _and_ the option `sendHttpHeaders` must be `true`. This behavior is to + * allow software to send its own headers (including the filename), and + * still use this library. + */ + public function __construct(?string $name = null, ?ArchiveOptions $opt = null) + { + $this->opt = $opt ?: new ArchiveOptions(); + + $this->output_name = $name; + $this->need_headers = $name && $this->opt->isSendHttpHeaders(); + + $this->cdr_ofs = new Bigint(); + $this->ofs = new Bigint(); + } + + /** + * addFile + * + * Add a file to the archive. + * + * @param String $name - path of file in archive (including directory). + * @param String $data - contents of file + * @param FileOptions $options + * + * File Options: + * time - Last-modified timestamp (seconds since the epoch) of + * this file. Defaults to the current time. + * comment - Comment related to this file. + * method - Storage method for file ("store" or "deflate") + * + * Examples: + * + * // add a file named 'foo.txt' + * $data = file_get_contents('foo.txt'); + * $zip->addFile('foo.txt', $data); + * + * // add a file named 'bar.jpg' with a comment and a last-modified + * // time of two hours ago + * $data = file_get_contents('bar.jpg'); + * $opt->setTime = time() - 2 * 3600; + * $opt->setComment = 'this is a comment about bar.jpg'; + * $zip->addFile('bar.jpg', $data, $opt); + */ + public function addFile(string $name, string $data, ?FileOptions $options = null): void + { + $options = $options ?: new FileOptions(); + $options->defaultTo($this->opt); + + $file = new File($this, $name, $options); + $file->processData($data); + } + + /** + * addFileFromPath + * + * Add a file at path to the archive. + * + * Note that large files may be compressed differently than smaller + * files; see the "Large File Support" section above for more + * information. + * + * @param String $name - name of file in archive (including directory path). + * @param String $path - path to file on disk (note: paths should be encoded using + * UNIX-style forward slashes -- e.g '/path/to/some/file'). + * @param FileOptions $options + * + * File Options: + * time - Last-modified timestamp (seconds since the epoch) of + * this file. Defaults to the current time. + * comment - Comment related to this file. + * method - Storage method for file ("store" or "deflate") + * + * Examples: + * + * // add a file named 'foo.txt' from the local file '/tmp/foo.txt' + * $zip->addFileFromPath('foo.txt', '/tmp/foo.txt'); + * + * // add a file named 'bigfile.rar' from the local file + * // '/usr/share/bigfile.rar' with a comment and a last-modified + * // time of two hours ago + * $path = '/usr/share/bigfile.rar'; + * $opt->setTime = time() - 2 * 3600; + * $opt->setComment = 'this is a comment about bar.jpg'; + * $zip->addFileFromPath('bigfile.rar', $path, $opt); + * + * @return void + * @throws \ZipStream\Exception\FileNotFoundException + * @throws \ZipStream\Exception\FileNotReadableException + */ + public function addFileFromPath(string $name, string $path, ?FileOptions $options = null): void + { + $options = $options ?: new FileOptions(); + $options->defaultTo($this->opt); + + $file = new File($this, $name, $options); + $file->processPath($path); + } + + /** + * addFileFromStream + * + * Add an open stream to the archive. + * + * @param String $name - path of file in archive (including directory). + * @param resource $stream - contents of file as a stream resource + * @param FileOptions $options + * + * File Options: + * time - Last-modified timestamp (seconds since the epoch) of + * this file. Defaults to the current time. + * comment - Comment related to this file. + * + * Examples: + * + * // create a temporary file stream and write text to it + * $fp = tmpfile(); + * fwrite($fp, 'The quick brown fox jumped over the lazy dog.'); + * + * // add a file named 'streamfile.txt' from the content of the stream + * $x->addFileFromStream('streamfile.txt', $fp); + * + * @return void + */ + public function addFileFromStream(string $name, $stream, ?FileOptions $options = null): void + { + $options = $options ?: new FileOptions(); + $options->defaultTo($this->opt); + + $file = new File($this, $name, $options); + $file->processStream(new DeflateStream($stream)); + } + + /** + * addFileFromPsr7Stream + * + * Add an open stream to the archive. + * + * @param String $name - path of file in archive (including directory). + * @param StreamInterface $stream - contents of file as a stream resource + * @param FileOptions $options + * + * File Options: + * time - Last-modified timestamp (seconds since the epoch) of + * this file. Defaults to the current time. + * comment - Comment related to this file. + * + * Examples: + * + * // create a temporary file stream and write text to it + * $fp = tmpfile(); + * fwrite($fp, 'The quick brown fox jumped over the lazy dog.'); + * + * // add a file named 'streamfile.txt' from the content of the stream + * $x->addFileFromPsr7Stream('streamfile.txt', $fp); + * + * @return void + */ + public function addFileFromPsr7Stream( + string $name, + StreamInterface $stream, + ?FileOptions $options = null + ): void { + $options = $options ?: new FileOptions(); + $options->defaultTo($this->opt); + + $file = new File($this, $name, $options); + $file->processStream($stream); + } + + /** + * finish + * + * Write zip footer to stream. + * + * Example: + * + * // add a list of files to the archive + * $files = array('foo.txt', 'bar.jpg'); + * foreach ($files as $path) + * $zip->addFile($path, file_get_contents($path)); + * + * // write footer to stream + * $zip->finish(); + * @return void + * + * @throws OverflowException + */ + public function finish(): void + { + // add trailing cdr file records + foreach ($this->files as $cdrFile) { + $this->send($cdrFile); + $this->cdr_ofs = $this->cdr_ofs->add(Bigint::init(strlen($cdrFile))); + } + + // Add 64bit headers (if applicable) + if (count($this->files) >= 0xFFFF || + $this->cdr_ofs->isOver32() || + $this->ofs->isOver32()) { + if (!$this->opt->isEnableZip64()) { + throw new OverflowException(); + } + + $this->addCdr64Eof(); + $this->addCdr64Locator(); + } + + // add trailing cdr eof record + $this->addCdrEof(); + + // The End + $this->clear(); + } + + /** + * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record. + * + * @return void + */ + protected function addCdr64Eof(): void + { + $num_files = count($this->files); + $cdr_length = $this->cdr_ofs; + $cdr_offset = $this->ofs; + + $fields = [ + ['V', static::ZIP64_CDR_EOF_SIGNATURE], // ZIP64 end of central file header signature + ['P', 44], // Length of data below this header (length of block - 12) = 44 + ['v', static::ZIP_VERSION_MADE_BY], // Made by version + ['v', Version::ZIP64], // Extract by version + ['V', 0x00], // disk number + ['V', 0x00], // no of disks + ['P', $num_files], // no of entries on disk + ['P', $num_files], // no of entries in cdr + ['P', $cdr_length], // CDR size + ['P', $cdr_offset], // CDR offset + ]; + + $ret = static::packFields($fields); + $this->send($ret); + } + + /** + * Create a format string and argument list for pack(), then call + * pack() and return the result. + * + * @param array $fields + * @return string + */ + public static function packFields(array $fields): string + { + $fmt = ''; + $args = []; + + // populate format string and argument list + foreach ($fields as [$format, $value]) { + if ($format === 'P') { + $fmt .= 'VV'; + if ($value instanceof Bigint) { + $args[] = $value->getLow32(); + $args[] = $value->getHigh32(); + } else { + $args[] = $value; + $args[] = 0; + } + } else { + if ($value instanceof Bigint) { + $value = $value->getLow32(); + } + $fmt .= $format; + $args[] = $value; + } + } + + // prepend format string to argument list + array_unshift($args, $fmt); + + // build output string from header and compressed data + return pack(...$args); + } + + /** + * Send string, sending HTTP headers if necessary. + * Flush output after write if configure option is set. + * + * @param String $str + * @return void + */ + public function send(string $str): void + { + if ($this->need_headers) { + $this->sendHttpHeaders(); + } + $this->need_headers = false; + + fwrite($this->opt->getOutputStream(), $str); + + if ($this->opt->isFlushOutput()) { + // flush output buffer if it is on and flushable + $status = ob_get_status(); + if (isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) { + ob_flush(); + } + + // Flush system buffers after flushing userspace output buffer + flush(); + } + } + + /** + * Send HTTP headers for this stream. + * + * @return void + */ + protected function sendHttpHeaders(): void + { + // grab content disposition + $disposition = $this->opt->getContentDisposition(); + + if ($this->output_name) { + // Various different browsers dislike various characters here. Strip them all for safety. + $safe_output = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->output_name)); + + // Check if we need to UTF-8 encode the filename + $urlencoded = rawurlencode($safe_output); + $disposition .= "; filename*=UTF-8''{$urlencoded}"; + } + + $headers = array( + 'Content-Type' => $this->opt->getContentType(), + 'Content-Disposition' => $disposition, + 'Pragma' => 'public', + 'Cache-Control' => 'public, must-revalidate', + 'Content-Transfer-Encoding' => 'binary' + ); + + $call = $this->opt->getHttpHeaderCallback(); + foreach ($headers as $key => $val) { + $call("$key: $val"); + } + } + + /** + * Send ZIP64 CDR Locator (Central Directory Record Locator) record. + * + * @return void + */ + protected function addCdr64Locator(): void + { + $cdr_offset = $this->ofs->add($this->cdr_ofs); + + $fields = [ + ['V', static::ZIP64_CDR_LOCATOR_SIGNATURE], // ZIP64 end of central file header signature + ['V', 0x00], // Disc number containing CDR64EOF + ['P', $cdr_offset], // CDR offset + ['V', 1], // Total number of disks + ]; + + $ret = static::packFields($fields); + $this->send($ret); + } + + /** + * Send CDR EOF (Central Directory Record End-of-File) record. + * + * @return void + */ + protected function addCdrEof(): void + { + $num_files = count($this->files); + $cdr_length = $this->cdr_ofs; + $cdr_offset = $this->ofs; + + // grab comment (if specified) + $comment = $this->opt->getComment(); + + $fields = [ + ['V', static::CDR_EOF_SIGNATURE], // end of central file header signature + ['v', 0x00], // disk number + ['v', 0x00], // no of disks + ['v', min($num_files, 0xFFFF)], // no of entries on disk + ['v', min($num_files, 0xFFFF)], // no of entries in cdr + ['V', $cdr_length->getLowFF()], // CDR size + ['V', $cdr_offset->getLowFF()], // CDR offset + ['v', strlen($comment)], // Zip Comment size + ]; + + $ret = static::packFields($fields) . $comment; + $this->send($ret); + } + + /** + * Clear all internal variables. Note that the stream object is not + * usable after this. + * + * @return void + */ + protected function clear(): void + { + $this->files = []; + $this->ofs = new Bigint(); + $this->cdr_ofs = new Bigint(); + $this->opt = new ArchiveOptions(); + } + + /** + * Is this file larger than large_file_size? + * + * @param string $path + * @return bool + */ + public function isLargeFile(string $path): bool + { + if (!$this->opt->isStatFiles()) { + return false; + } + $stat = stat($path); + return $stat['size'] > $this->opt->getLargeFileSize(); + } + + /** + * Save file attributes for trailing CDR record. + * + * @param File $file + * @return void + */ + public function addToCdr(File $file): void + { + $file->ofs = $this->ofs; + $this->ofs = $this->ofs->add($file->getTotalLength()); + $this->files[] = $file->getCdrFile(); + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/BigintTest.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/BigintTest.php new file mode 100644 index 00000000000..ac9c7c28ba0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/BigintTest.php @@ -0,0 +1,65 @@ +assertSame('0x0000000012345678', $bigint->getHex64()); + $this->assertSame(0x12345678, $bigint->getLow32()); + $this->assertSame(0, $bigint->getHigh32()); + } + + public function testConstructLarge(): void + { + $bigint = new Bigint(0x87654321); + $this->assertSame('0x0000000087654321', $bigint->getHex64()); + $this->assertSame('87654321', bin2hex(pack('N', $bigint->getLow32()))); + $this->assertSame(0, $bigint->getHigh32()); + } + + public function testAddSmallValue(): void + { + $bigint = new Bigint(1); + $bigint = $bigint->add(Bigint::init(2)); + $this->assertSame(3, $bigint->getLow32()); + $this->assertFalse($bigint->isOver32()); + $this->assertTrue($bigint->isOver32(true)); + $this->assertSame($bigint->getLowFF(), (float)$bigint->getLow32()); + $this->assertSame($bigint->getLowFF(true), (float)0xFFFFFFFF); + } + + public function testAddWithOverflowAtLowestByte(): void + { + $bigint = new Bigint(0xFF); + $bigint = $bigint->add(Bigint::init(0x01)); + $this->assertSame(0x100, $bigint->getLow32()); + } + + public function testAddWithOverflowAtInteger32(): void + { + $bigint = new Bigint(0xFFFFFFFE); + $this->assertFalse($bigint->isOver32()); + $bigint = $bigint->add(Bigint::init(0x01)); + $this->assertTrue($bigint->isOver32()); + $bigint = $bigint->add(Bigint::init(0x01)); + $this->assertSame('0x0000000100000000', $bigint->getHex64()); + $this->assertTrue($bigint->isOver32()); + $this->assertSame((float)0xFFFFFFFF, $bigint->getLowFF()); + } + + public function testAddWithOverflowAtInteger64(): void + { + $bigint = Bigint::fromLowHigh(0xFFFFFFFF, 0xFFFFFFFF); + $this->assertSame('0xFFFFFFFFFFFFFFFF', $bigint->getHex64()); + $this->expectException(OverflowException::class); + $bigint->add(Bigint::init(1)); + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/ZipStreamTest.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/ZipStreamTest.php new file mode 100644 index 00000000000..6549b0b4691 --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/ZipStreamTest.php @@ -0,0 +1,586 @@ +expectException(\ZipStream\Exception\FileNotFoundException::class); + // Get ZipStream Object + $zip = new ZipStream(); + + // Trigger error by adding a file which doesn't exist + $zip->addFileFromPath('foobar.php', '/foo/bar/foobar.php'); + } + + public function testFileNotReadableException(): void + { + // create new virtual filesystem + $root = vfsStream::setup('vfs'); + // create a virtual file with no permissions + $file = vfsStream::newFile('foo.txt', 0000)->at($root)->setContent('bar'); + $zip = new ZipStream(); + $this->expectException(\ZipStream\Exception\FileNotReadableException::class); + $zip->addFileFromPath('foo.txt', $file->url()); + } + + public function testDostime(): void + { + // Allows testing of protected method + $class = new \ReflectionClass(File::class); + $method = $class->getMethod('dostime'); + $method->setAccessible(true); + + $this->assertSame($method->invoke(null, 1416246368), 1165069764); + + // January 1 1980 - DOS Epoch. + $this->assertSame($method->invoke(null, 315532800), 2162688); + + // January 1 1970 -> January 1 1980 due to minimum DOS Epoch. @todo Throw Exception? + $this->assertSame($method->invoke(null, 0), 2162688); + } + + public function testAddFile(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $zip->addFile('sample.txt', 'Sample String Data'); + $zip->addFile('test/sample.txt', 'More Simple Sample Data'); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(['sample.txt', 'test/sample.txt'], $files); + + $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data'); + $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data'); + } + + /** + * @return array + */ + protected function getTmpFileStream(): array + { + $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest'); + $stream = fopen($tmp, 'wb+'); + + return array($tmp, $stream); + } + + /** + * @param string $tmp + * @return string + */ + protected function validateAndExtractZip($tmp): string + { + $tmpDir = $this->getTmpDir(); + + $zipArch = new \ZipArchive; + $res = $zipArch->open($tmp); + + if ($res !== true) { + $this->fail("Failed to open {$tmp}. Code: $res"); + + return $tmpDir; + } + + $this->assertEquals(0, $zipArch->status); + $this->assertEquals(0, $zipArch->statusSys); + + $zipArch->extractTo($tmpDir); + $zipArch->close(); + + return $tmpDir; + } + + protected function getTmpDir(): string + { + $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest'); + unlink($tmp); + mkdir($tmp) or $this->fail('Failed to make directory'); + + return $tmp; + } + + /** + * @param string $path + * @return string[] + */ + protected function getRecursiveFileList(string $path): array + { + $data = array(); + $path = (string)realpath($path); + $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); + + $pathLen = strlen($path); + foreach ($files as $file) { + $filePath = $file->getRealPath(); + if (!is_dir($filePath)) { + $data[] = substr($filePath, $pathLen + 1); + } + } + + sort($data); + + return $data; + } + + public function testAddFileUtf8NameComment(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $name = 'árvíztűrő tükörfúrógép.txt'; + $content = 'Sample String Data'; + $comment = + 'Filename has every special characters ' . + 'from Hungarian language in lowercase. ' . + 'In uppercase: ÁÍŰŐÜÖÚÓÉ'; + + $fileOptions = new FileOptions(); + $fileOptions->setComment($comment); + + $zip->addFile($name, $content, $fileOptions); + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array($name), $files); + $this->assertStringEqualsFile($tmpDir . '/' . $name, $content); + + $zipArch = new \ZipArchive(); + $zipArch->open($tmp); + $this->assertEquals($comment, $zipArch->getCommentName($name)); + } + + public function testAddFileUtf8NameNonUtfComment(): void + { + $this->expectException(\ZipStream\Exception\EncodingException::class); + + $stream = $this->getTmpFileStream()[1]; + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $name = 'á.txt'; + $content = 'any'; + $comment = 'á'; + + $fileOptions = new FileOptions(); + $fileOptions->setComment(mb_convert_encoding($comment, 'ISO-8859-2', 'UTF-8')); + + $zip->addFile($name, $content, $fileOptions); + } + + public function testAddFileNonUtf8NameUtfComment(): void + { + $this->expectException(\ZipStream\Exception\EncodingException::class); + + $stream = $this->getTmpFileStream()[1]; + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $name = 'á.txt'; + $content = 'any'; + $comment = 'á'; + + $fileOptions = new FileOptions(); + $fileOptions->setComment($comment); + + $zip->addFile(mb_convert_encoding($name, 'ISO-8859-2', 'UTF-8'), $content, $fileOptions); + } + + public function testAddFileWithStorageMethod(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + + $zip->addFile('sample.txt', 'Sample String Data', $fileOptions); + $zip->addFile('test/sample.txt', 'More Simple Sample Data'); + $zip->finish(); + fclose($stream); + + $zipArch = new \ZipArchive(); + $zipArch->open($tmp); + + $sample1 = $zipArch->statName('sample.txt'); + $sample12 = $zipArch->statName('test/sample.txt'); + $this->assertEquals($sample1['comp_method'], Method::STORE); + $this->assertEquals($sample12['comp_method'], Method::DEFLATE); + + $zipArch->close(); + } + + public function testDecompressFileWithMacUnarchiver(): void + { + if (!file_exists(self::OSX_ARCHIVE_UTILITY)) { + $this->markTestSkipped('The Mac OSX Archive Utility is not available.'); + } + + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $folder = uniqid('', true); + + $zip->addFile($folder . '/sample.txt', 'Sample Data'); + $zip->finish(); + fclose($stream); + + exec(escapeshellarg(self::OSX_ARCHIVE_UTILITY) . ' ' . escapeshellarg($tmp), $output, $returnStatus); + + $this->assertEquals(0, $returnStatus); + $this->assertCount(0, $output); + + $this->assertFileExists(dirname($tmp) . '/' . $folder . '/sample.txt'); + $this->assertStringEqualsFile(dirname($tmp) . '/' . $folder . '/sample.txt', 'Sample Data'); + } + + public function testAddFileFromPath(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + [$tmpExample, $streamExample] = $this->getTmpFileStream(); + fwrite($streamExample, 'Sample String Data'); + fclose($streamExample); + $zip->addFileFromPath('sample.txt', $tmpExample); + + [$tmpExample, $streamExample] = $this->getTmpFileStream(); + fwrite($streamExample, 'More Simple Sample Data'); + fclose($streamExample); + $zip->addFileFromPath('test/sample.txt', $tmpExample); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files); + + $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data'); + $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data'); + } + + public function testAddFileFromPathWithStorageMethod(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + + [$tmpExample, $streamExample] = $this->getTmpFileStream(); + fwrite($streamExample, 'Sample String Data'); + fclose($streamExample); + $zip->addFileFromPath('sample.txt', $tmpExample, $fileOptions); + + [$tmpExample, $streamExample] = $this->getTmpFileStream(); + fwrite($streamExample, 'More Simple Sample Data'); + fclose($streamExample); + $zip->addFileFromPath('test/sample.txt', $tmpExample); + + $zip->finish(); + fclose($stream); + + $zipArch = new \ZipArchive(); + $zipArch->open($tmp); + + $sample1 = $zipArch->statName('sample.txt'); + $this->assertEquals(Method::STORE, $sample1['comp_method']); + + $sample2 = $zipArch->statName('test/sample.txt'); + $this->assertEquals(Method::DEFLATE, $sample2['comp_method']); + + $zipArch->close(); + } + + public function testAddLargeFileFromPath(): void + { + $methods = [Method::DEFLATE(), Method::STORE()]; + $falseTrue = [false, true]; + foreach ($methods as $method) { + foreach ($falseTrue as $zeroHeader) { + foreach ($falseTrue as $zip64) { + if ($zeroHeader && $method->equals(Method::DEFLATE())) { + continue; + } + $this->addLargeFileFileFromPath($method, $zeroHeader, $zip64); + } + } + } + } + + protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + $options->setLargeFileMethod($method); + $options->setLargeFileSize(5); + $options->setZeroHeader($zeroHeader); + $options->setEnableZip64($zip64); + + $zip = new ZipStream(null, $options); + + [$tmpExample, $streamExample] = $this->getTmpFileStream(); + for ($i = 0; $i <= 10000; $i++) { + fwrite($streamExample, sha1((string)$i)); + if ($i % 100 === 0) { + fwrite($streamExample, "\n"); + } + } + fclose($streamExample); + $shaExample = sha1_file($tmpExample); + $zip->addFileFromPath('sample.txt', $tmpExample); + unlink($tmpExample); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array('sample.txt'), $files); + + $this->assertEquals(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}"); + } + + public function testAddFileFromStream(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + // In this test we can't use temporary stream to feed data + // because zlib.deflate filter gives empty string before PHP 7 + // it works fine with file stream + $streamExample = fopen(__FILE__, 'rb'); + $zip->addFileFromStream('sample.txt', $streamExample); +// fclose($streamExample); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + + $streamExample2 = fopen('php://temp', 'wb+'); + fwrite($streamExample2, 'More Simple Sample Data'); + rewind($streamExample2); // move the pointer back to the beginning of file. + $zip->addFileFromStream('test/sample.txt', $streamExample2, $fileOptions); +// fclose($streamExample2); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files); + + $this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt')); + $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data'); + } + + public function testAddFileFromStreamWithStorageMethod(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + + $streamExample = fopen('php://temp', 'wb+'); + fwrite($streamExample, 'Sample String Data'); + rewind($streamExample); // move the pointer back to the beginning of file. + $zip->addFileFromStream('sample.txt', $streamExample, $fileOptions); +// fclose($streamExample); + + $streamExample2 = fopen('php://temp', 'bw+'); + fwrite($streamExample2, 'More Simple Sample Data'); + rewind($streamExample2); // move the pointer back to the beginning of file. + $zip->addFileFromStream('test/sample.txt', $streamExample2); +// fclose($streamExample2); + + $zip->finish(); + fclose($stream); + + $zipArch = new \ZipArchive(); + $zipArch->open($tmp); + + $sample1 = $zipArch->statName('sample.txt'); + $this->assertEquals(Method::STORE, $sample1['comp_method']); + + $sample2 = $zipArch->statName('test/sample.txt'); + $this->assertEquals(Method::DEFLATE, $sample2['comp_method']); + + $zipArch->close(); + } + + public function testAddFileFromPsr7Stream(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $body = 'Sample String Data'; + $response = new Response(200, [], $body); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + + $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions); + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array('sample.json'), $files); + $this->assertStringEqualsFile($tmpDir . '/sample.json', $body); + } + + public function testAddFileFromPsr7StreamWithFileSizeSet(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + + $zip = new ZipStream(null, $options); + + $body = 'Sample String Data'; + $fileSize = strlen($body); + // Add fake padding + $fakePadding = "\0\0\0\0\0\0"; + $response = new Response(200, [], $body . $fakePadding); + + $fileOptions = new FileOptions(); + $fileOptions->setMethod(Method::STORE()); + $fileOptions->setSize($fileSize); + $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions); + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(array('sample.json'), $files); + $this->assertStringEqualsFile($tmpDir . '/sample.json', $body); + } + + public function testCreateArchiveWithFlushOptionSet(): void + { + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + $options->setFlushOutput(true); + + $zip = new ZipStream(null, $options); + + $zip->addFile('sample.txt', 'Sample String Data'); + $zip->addFile('test/sample.txt', 'More Simple Sample Data'); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + + $files = $this->getRecursiveFileList($tmpDir); + $this->assertEquals(['sample.txt', 'test/sample.txt'], $files); + + $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data'); + $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data'); + } + + public function testCreateArchiveWithOutputBufferingOffAndFlushOptionSet(): void + { + // WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering + ob_end_flush(); + $this->assertEquals(0, ob_get_level()); + + [$tmp, $stream] = $this->getTmpFileStream(); + + $options = new ArchiveOptions(); + $options->setOutputStream($stream); + $options->setFlushOutput(true); + + $zip = new ZipStream(null, $options); + + $zip->addFile('sample.txt', 'Sample String Data'); + + $zip->finish(); + fclose($stream); + + $tmpDir = $this->validateAndExtractZip($tmp); + $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data'); + + // WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing + ob_start(); + } +} diff --git a/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/bootstrap.php b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/bootstrap.php new file mode 100644 index 00000000000..b43c9bd6cbb --- /dev/null +++ b/lib/phpspreadsheet/vendor/maennchen/zipstream-php/test/bootstrap.php @@ -0,0 +1,6 @@ +setOutputStream(fopen('php://memory', 'wb')); + $fileOpt->setTime(clone $expectedTime); + + $zip = new ZipStream(null, $archiveOpt); + + $zip->addFile('sample.txt', 'Sample', $fileOpt); + + $zip->finish(); + + $this->assertEquals($expectedTime, $fileOpt->getTime()); + } +} diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php index e6e03ac0e9b..792ecef0d97 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/Autoloader.php @@ -8,7 +8,7 @@ namespace Complex; * * @package Complex * @copyright Copyright (c) 2014 Mark Baker (https://github.com/MarkBaker/PHPComplex) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @license https://opensource.org/licenses/MIT MIT */ class Autoloader { diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php index 5671eb28e2b..f7ba162188b 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/Complex.php @@ -176,8 +176,11 @@ class Complex list($realPart, $imaginaryPart, $suffix) = self::parseComplex($realPart); } } - if ($imaginaryPart <> 0.0 && empty($suffix)) { + + if ($imaginaryPart != 0.0 && empty($suffix)) { $suffix = 'i'; + } elseif ($imaginaryPart == 0.0 && !empty($suffix)) { + $suffix = ''; } // Set parsed values in our properties @@ -191,7 +194,7 @@ class Complex * * @return Float */ - public function getReal() + public function getReal(): float { return $this->realPart; } @@ -201,7 +204,7 @@ class Complex * * @return Float */ - public function getImaginary() + public function getImaginary(): float { return $this->imaginaryPart; } @@ -211,7 +214,7 @@ class Complex * * @return String */ - public function getSuffix() + public function getSuffix(): string { return $this->suffix; } @@ -221,7 +224,7 @@ class Complex * * @return Bool */ - public function isReal() + public function isReal(): bool { return $this->imaginaryPart == 0.0; } @@ -231,12 +234,12 @@ class Complex * * @return Bool */ - public function isComplex() + public function isComplex(): bool { return !$this->isReal(); } - public function format() + public function format(): string { $str = ""; if ($this->imaginaryPart != 0.0) { @@ -259,7 +262,7 @@ class Complex return $str; } - public function __toString() + public function __toString(): string { return $this->format(); } @@ -271,7 +274,7 @@ class Complex * @return Complex * @throws Exception If the argument isn't a Complex number or cannot be converted to one */ - public static function validateComplexArgument($complex) + public static function validateComplexArgument($complex): Complex { if (is_scalar($complex) || is_array($complex)) { $complex = new Complex($complex); @@ -287,7 +290,7 @@ class Complex * * @return Complex */ - public function reverse() + public function reverse(): Complex { return new Complex( $this->imaginaryPart, @@ -296,7 +299,7 @@ class Complex ); } - public function invertImaginary() + public function invertImaginary(): Complex { return new Complex( $this->realPart, @@ -305,7 +308,7 @@ class Complex ); } - public function invertReal() + public function invertReal(): Complex { return new Complex( $this->realPart * -1, @@ -373,15 +376,15 @@ class Complex $functionName = strtolower(str_replace('_', '', $functionName)); // Test for function calls - if (in_array($functionName, self::$functions)) { + if (in_array($functionName, self::$functions, true)) { $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; return $functionName($this, ...$arguments); } // Test for operation calls - if (in_array($functionName, self::$operations)) { + if (in_array($functionName, self::$operations, true)) { $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; return $functionName($this, ...$arguments); } - throw new Exception('Function or Operation does not exist'); + throw new Exception('Complex Function or Operation does not exist'); } } diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php index b66ef7cf0e9..6e2729a891e 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/abs.php @@ -23,7 +23,7 @@ namespace Complex; * @see rho * */ -function abs($complex) +function abs($complex): float { return rho($complex); } diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acos.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acos.php index a269c3df536..19e8c2a1c6b 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acos.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acos.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The inverse cosine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function acos($complex) +function acos($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php index 11bee4666bd..4ddc2ddb37c 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acot.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function acot($complex) +function acot($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acoth.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acoth.php index b76014f6077..9939a462487 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acoth.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acoth.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function acoth($complex) +function acoth($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsc.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsc.php index 65ef4bb601e..dd5d5c80af9 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsc.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsc.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function acsc($complex) +function acsc($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return asin(inverse($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php index bb45d34775d..66d9bdf2c09 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/acsch.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function acsch($complex) +function acsch($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return asinh(inverse($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php index d7209cc4849..17217bb3073 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/argument.php @@ -22,7 +22,7 @@ namespace Complex; * * @see theta */ -function argument($complex) +function argument($complex): float { return theta($complex); } diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asec.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asec.php index c8b0647781d..d4eb1f7d437 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asec.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asec.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function asec($complex) +function asec($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return acos(inverse($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php index b36c40e2ca7..929b0d19f1a 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asech.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function asech($complex) +function asech($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return acosh(inverse($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php index 9c982aca8ea..b675046a80e 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asin.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The inverse sine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function asin($complex) +function asin($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php index c1243fd7f50..3e5c2944fee 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/asinh.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The inverse hyperbolic sine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function asinh($complex) +function asinh($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php index 2c75dcf8e45..ecbea801c58 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atan.php @@ -20,7 +20,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function atan($complex) +function atan($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php index c53f2a9a087..189493b8831 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/atanh.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The inverse hyperbolic tangent of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function atanh($complex) +function atanh($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php index bd1984b7840..52666176e11 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/conjugate.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The conjugate of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function conjugate($complex) +function conjugate($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php index 80a4683d599..0c6ea1a8e49 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cos.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The cosine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function cos($complex) +function cos($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php index a4bea65369d..ee674c27053 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cosh.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The hyperbolic cosine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function cosh($complex) +function cosh($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php index 339101e1b12..693d0f7c5c2 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/cot.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function cot($complex) +function cot($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php index 7fe705a4dbb..1ff1ad50627 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/coth.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function coth($complex) +function coth($complex): Complex { $complex = Complex::validateComplexArgument($complex); return inverse(tanh($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csc.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csc.php index bfbbc6ce942..8017bb56739 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csc.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csc.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function csc($complex) +function csc($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return inverse(sin($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php index f4500981dbc..acaa6c0cd8b 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/csch.php @@ -17,12 +17,12 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function csch($complex) +function csch($complex): Complex { $complex = Complex::validateComplexArgument($complex); if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) { - return INF; + return new Complex(INF); } return inverse(sinh($complex)); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php index 4cac6967ec4..8ab3b3e5968 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/exp.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The exponential of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function exp($complex) +function exp($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php index 7d3182adad4..563e3fd8dc6 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/inverse.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function inverse($complex) +function inverse($complex): Complex { $complex = clone Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php index 39071cf67e5..d57bb7af586 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/ln.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If the real and the imaginary parts are both zero */ -function ln($complex) +function ln($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php index 694d3d08eaf..ce4d001fc2e 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log10.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If the real and the imaginary parts are both zero */ -function log10($complex) +function log10($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php index 081f2c499f1..21b3e2aabb5 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/log2.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If the real and the imaginary parts are both zero */ -function log2($complex) +function log2($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php index dbd11922cb2..232e178a076 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/negative.php @@ -13,13 +13,13 @@ namespace Complex; * Returns the negative of a complex number. * * @param Complex|mixed $complex Complex number or a numeric value. - * @return float The negative value of the complex argument. + * @return Complex The negative value of the complex argument. * @throws Exception If argument isn't a valid real or complex number. * * @see rho * */ -function negative($complex) +function negative($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php index 18ee2690951..da3340fe1bb 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/pow.php @@ -17,7 +17,7 @@ namespace Complex; * @return Complex The complex argument raised to the real power. * @throws Exception If the power argument isn't a valid real */ -function pow($complex, $power) +function pow($complex, $power): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php index 750f3f99e2a..d5264a769ff 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/rho.php @@ -17,7 +17,7 @@ namespace Complex; * @return float The rho value of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function rho($complex) +function rho($complex): float { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php index 7dd43eaf037..1c7768d4ff7 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sec.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function sec($complex) +function sec($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sech.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sech.php index 8e40a38fdec..a86bb474e5d 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sech.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sech.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function sech($complex) +function sech($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sin.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sin.php index 2895d6f8298..14423b9170d 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sin.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sin.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The sine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function sin($complex) +function sin($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php index 4c0f6503e3e..f051a8e2a59 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sinh.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The hyperbolic sine of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function sinh($complex) +function sinh($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php index 9c171b880b1..17c19c70bf9 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/sqrt.php @@ -16,7 +16,7 @@ namespace Complex; * @return Complex The Square root of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function sqrt($complex) +function sqrt($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php index 014d7981b47..6996a3a622a 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tan.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function tan($complex) +function tan($complex): Complex { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php index 028741d6be7..a401042ad5c 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/tanh.php @@ -17,7 +17,7 @@ namespace Complex; * @throws Exception If argument isn't a valid real or complex number. * @throws \InvalidArgumentException If function would result in a division by zero */ -function tanh($complex) +function tanh($complex): Complex { $complex = Complex::validateComplexArgument($complex); $real = $complex->getReal(); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php index d12866cd83f..f022abb4ec9 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/functions/theta.php @@ -17,7 +17,7 @@ namespace Complex; * @return float The theta value of the complex argument. * @throws Exception If argument isn't a valid real or complex number. */ -function theta($complex) +function theta($complex): float { $complex = Complex::validateComplexArgument($complex); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php index 10bd42f4f60..6963bb0450a 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/add.php @@ -15,7 +15,7 @@ namespace Complex; * @param array of string|integer|float|Complex $complexValues The numbers to add * @return Complex */ -function add(...$complexValues) +function add(...$complexValues): Complex { if (count($complexValues) < 2) { throw new \Exception('This function requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php index 089e0ef99e4..a680931cb20 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideby.php @@ -15,7 +15,7 @@ namespace Complex; * @param array of string|integer|float|Complex $complexValues The numbers to divide * @return Complex */ -function divideby(...$complexValues) +function divideby(...$complexValues): Complex { if (count($complexValues) < 2) { throw new \Exception('This function requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php index 3dfe085e20c..70869933efd 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/divideinto.php @@ -15,7 +15,7 @@ namespace Complex; * @param array of string|integer|float|Complex $complexValues The numbers to divide * @return Complex */ -function divideinto(...$complexValues) +function divideinto(...$complexValues): Complex { if (count($complexValues) < 2) { throw new \Exception('This function requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php index bf2473ea78e..06a52b2781f 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/multiply.php @@ -15,7 +15,7 @@ namespace Complex; * @param array of string|integer|float|Complex $complexValues The numbers to multiply * @return Complex */ -function multiply(...$complexValues) +function multiply(...$complexValues): Complex { if (count($complexValues) < 2) { throw new \Exception('This function requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php index 075ef443698..0d9985cc534 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php +++ b/lib/phpspreadsheet/vendor/markbaker/complex/classes/src/operations/subtract.php @@ -15,7 +15,7 @@ namespace Complex; * @param array of string|integer|float|Complex $complexValues The numbers to subtract * @return Complex */ -function subtract(...$complexValues) +function subtract(...$complexValues): Complex { if (count($complexValues) < 2) { throw new \Exception('This function requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/complex/composer.json b/lib/phpspreadsheet/vendor/markbaker/complex/composer.json index 91e9e231885..a343d6e8708 100644 --- a/lib/phpspreadsheet/vendor/markbaker/complex/composer.json +++ b/lib/phpspreadsheet/vendor/markbaker/complex/composer.json @@ -12,17 +12,17 @@ } ], "require": { - "php": "^5.6.0|^7.0.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.4.0", - "phpdocumentor/phpdocumentor":"2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "phpdocumentor/phpdocumentor": "2.*", "phpmd/phpmd": "2.*", - "sebastian/phpcpd": "2.*", - "phploc/phploc": "2.*", - "squizlabs/php_codesniffer": "^3.3.0", - "phpcompatibility/php-compatibility": "^8.0", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3" + "sebastian/phpcpd": "^4.0", + "phploc/phploc": "^4.0", + "squizlabs/php_codesniffer": "^3.4", + "phpcompatibility/php-compatibility": "^9.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0" }, "autoload": { "psr-4": { @@ -74,18 +74,11 @@ ] }, "scripts": { - "style": [ - "phpcs --report-width=200 --report-summary --report-full classes/src/ --standard=PSR2 -n" - ], - "mess": [ - "phpmd classes/src/ xml codesize,unusedcode,design,naming -n" - ], - "lines": [ - "phploc classes/src/ -n" - ], - "cpd": [ - "phpcpd classes/src/ -n" - ] + "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n", + "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n", + "lines": "phploc classes/src/ -n", + "cpd": "phpcpd classes/src/ -n", + "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n" }, "minimum-stability": "dev" -} \ No newline at end of file +} diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php index a4f0495179f..e4e3140d12e 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Matrix.php @@ -55,7 +55,7 @@ class Matrix * * @param array $grid */ - protected function buildFromArray(array $grid) + protected function buildFromArray(array $grid): void { $this->rows = count($grid); $columns = array_reduce( @@ -86,7 +86,7 @@ class Matrix * @return int * @throws Exception */ - public static function validateRow($row) + public static function validateRow(int $row): int { if ((!is_numeric($row)) || (intval($row) < 1)) { throw new Exception('Invalid Row'); @@ -102,7 +102,7 @@ class Matrix * @return int * @throws Exception */ - public static function validateColumn($column) + public static function validateColumn(int $column): int { if ((!is_numeric($column)) || (intval($column) < 1)) { throw new Exception('Invalid Column'); @@ -118,7 +118,7 @@ class Matrix * @return int * @throws Exception */ - protected function validateRowInRange($row) + protected function validateRowInRange(int $row): int { $row = static::validateRow($row); if ($row > $this->rows) { @@ -135,7 +135,7 @@ class Matrix * @return int * @throws Exception */ - protected function validateColumnInRange($column) + protected function validateColumnInRange(int $column): int { $column = static::validateColumn($column); if ($column > $this->columns) { @@ -157,7 +157,7 @@ class Matrix * @return static * @throws Exception */ - public function getRows($row, $rowCount = 1) + public function getRows(int $row, int $rowCount = 1): Matrix { $row = $this->validateRowInRange($row); if ($rowCount === 0) { @@ -179,7 +179,7 @@ class Matrix * @return Matrix * @throws Exception */ - public function getColumns($column, $columnCount = 1) + public function getColumns(int $column, int $columnCount = 1): Matrix { $column = $this->validateColumnInRange($column); if ($columnCount < 1) { @@ -207,7 +207,7 @@ class Matrix * @return static * @throws Exception */ - public function dropRows($row, $rowCount = 1) + public function dropRows(int $row, int $rowCount = 1): Matrix { $this->validateRowInRange($row); if ($rowCount === 0) { @@ -233,7 +233,7 @@ class Matrix * @return static * @throws Exception */ - public function dropColumns($column, $columnCount = 1) + public function dropColumns(int $column, int $columnCount = 1): Matrix { $this->validateColumnInRange($column); if ($columnCount < 1) { @@ -260,7 +260,7 @@ class Matrix * @return mixed * @throws Exception */ - public function getValue($row, $column) + public function getValue(int $row, int $column) { $row = $this->validateRowInRange($row); $column = $this->validateColumnInRange($column); @@ -274,7 +274,7 @@ class Matrix * * @return \Generator|Matrix[]|mixed[] */ - public function rows() + public function rows(): \Generator { foreach ($this->grid as $i => $row) { yield $i + 1 => ($this->columns == 1) @@ -289,7 +289,7 @@ class Matrix * * @return \Generator|Matrix[]|mixed[] */ - public function columns() + public function columns(): \Generator { for ($i = 0; $i < $this->columns; ++$i) { yield $i + 1 => ($this->rows == 1) @@ -304,7 +304,7 @@ class Matrix * * @return bool */ - public function isSquare() + public function isSquare(): bool { return $this->rows == $this->columns; } @@ -315,7 +315,7 @@ class Matrix * * @return bool */ - public function isVector() + public function isVector(): bool { return $this->rows == 1 || $this->columns == 1; } @@ -325,7 +325,7 @@ class Matrix * * @return array */ - public function toArray() + public function toArray(): array { return $this->grid; } @@ -342,7 +342,7 @@ class Matrix * @return mixed * @throws Exception */ - public function __get($propertyName) + public function __get(string $propertyName) { $propertyName = strtolower($propertyName); @@ -384,11 +384,11 @@ class Matrix * @return Matrix|float * @throws Exception */ - public function __call($functionName, $arguments) + public function __call(string $functionName, $arguments) { $functionName = strtolower(str_replace('_', '', $functionName)); - if (in_array($functionName, self::$functions) || in_array($functionName, self::$operations)) { + if (in_array($functionName, self::$functions, true) || in_array($functionName, self::$operations, true)) { $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}"; if (is_callable($functionName)) { $arguments = array_values(array_merge([$this], $arguments)); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Addition.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Addition.php index e78c6d7d906..543f56e959a 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Addition.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Addition.php @@ -14,7 +14,7 @@ class Addition extends Operator * @throws Exception If the provided argument is not appropriate for the operation * @return $this The operation object, allowing multiple additions to be chained **/ - public function execute($value) + public function execute($value): Operator { if (is_array($value)) { $value = new Matrix($value); @@ -35,7 +35,7 @@ class Addition extends Operator * @param mixed $value The numeric value to add to the current base value * @return $this The operation object, allowing multiple additions to be chained **/ - protected function addScalar($value) + protected function addScalar($value): Operator { for ($row = 0; $row < $this->rows; ++$row) { for ($column = 0; $column < $this->columns; ++$column) { @@ -53,7 +53,7 @@ class Addition extends Operator * @return $this The operation object, allowing multiple additions to be chained * @throws Exception If the provided argument is not appropriate for the operation **/ - protected function addMatrix(Matrix $value) + protected function addMatrix(Matrix $value): Operator { $this->validateMatchingDimensions($value); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php index 6db0853bf1e..cc51ef961dc 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/DirectSum.php @@ -14,7 +14,7 @@ class DirectSum extends Operator * @return $this The operation object, allowing multiple additions to be chained * @throws Exception If the provided argument is not appropriate for the operation */ - public function execute($value) + public function execute($value): Operator { if (is_array($value)) { $value = new Matrix($value); @@ -33,7 +33,7 @@ class DirectSum extends Operator * @param Matrix $value The numeric value to concatenate/direct sum with the current base value * @return $this The operation object, allowing multiple additions to be chained **/ - private function directSumMatrix($value) + private function directSumMatrix($value): Operator { $originalColumnCount = count($this->matrix[0]); $originalRowCount = count($this->matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php index 2a573f5500e..b262f596ae2 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Division.php @@ -15,7 +15,7 @@ class Division extends Multiplication * @throws Exception If the provided argument is not appropriate for the operation * @return $this The operation object, allowing multiple divisions to be chained **/ - public function execute($value) + public function execute($value): Operator { if (is_array($value)) { $value = new Matrix($value); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php index 63df162d6b0..e75d0ade17b 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Multiplication.php @@ -15,7 +15,7 @@ class Multiplication extends Operator * @throws Exception If the provided argument is not appropriate for the operation * @return $this The operation object, allowing multiple multiplications to be chained **/ - public function execute($value) + public function execute($value): Operator { if (is_array($value)) { $value = new Matrix($value); @@ -36,7 +36,7 @@ class Multiplication extends Operator * @param mixed $value The numeric value to multiply with the current base value * @return $this The operation object, allowing multiple mutiplications to be chained **/ - protected function multiplyScalar($value) + protected function multiplyScalar($value): Operator { for ($row = 0; $row < $this->rows; ++$row) { for ($column = 0; $column < $this->columns; ++$column) { @@ -54,7 +54,7 @@ class Multiplication extends Operator * @return $this The operation object, allowing multiple mutiplications to be chained * @throws Exception If the provided argument is not appropriate for the operation **/ - protected function multiplyMatrix(Matrix $value) + protected function multiplyMatrix(Matrix $value): Operator { $this->validateReflectingDimensions($value); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Operator.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Operator.php index 87d3f3b5d77..39e36c61935 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Operator.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Operator.php @@ -46,7 +46,7 @@ abstract class Operator * @param Matrix $matrix The second Matrix object on which the operation will be performed * @throws Exception */ - protected function validateMatchingDimensions(Matrix $matrix) + protected function validateMatchingDimensions(Matrix $matrix): void { if (($this->rows != $matrix->rows) || ($this->columns != $matrix->columns)) { throw new Exception('Matrices have mismatched dimensions'); @@ -59,7 +59,7 @@ abstract class Operator * @param Matrix $matrix The second Matrix object on which the operation will be performed * @throws Exception */ - protected function validateReflectingDimensions(Matrix $matrix) + protected function validateReflectingDimensions(Matrix $matrix): void { if ($this->columns != $matrix->rows) { throw new Exception('Matrices have mismatched dimensions'); @@ -71,7 +71,7 @@ abstract class Operator * * @return Matrix */ - public function result() + public function result(): Matrix { return new Matrix($this->matrix); } diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php index 57c0b147c19..b7e14fad4c7 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/Operators/Subtraction.php @@ -14,7 +14,7 @@ class Subtraction extends Operator * @throws Exception If the provided argument is not appropriate for the operation * @return $this The operation object, allowing multiple subtractions to be chained **/ - public function execute($value) + public function execute($value): Operator { if (is_array($value)) { $value = new Matrix($value); @@ -35,7 +35,7 @@ class Subtraction extends Operator * @param mixed $value The numeric value to subtracted from the current base value * @return $this The operation object, allowing multiple additions to be chained **/ - protected function subtractScalar($value) + protected function subtractScalar($value): Operator { for ($row = 0; $row < $this->rows; ++$row) { for ($column = 0; $column < $this->columns; ++$column) { @@ -53,7 +53,7 @@ class Subtraction extends Operator * @return $this The operation object, allowing multiple subtractions to be chained * @throws Exception If the provided argument is not appropriate for the operation **/ - protected function subtractMatrix(Matrix $value) + protected function subtractMatrix(Matrix $value): Operator { $this->validateMatchingDimensions($value); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php index fc1e16995ca..ec1933f11ef 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/adjoint.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function adjoint($matrix) +function adjoint($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/antidiagonal.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/antidiagonal.php index cc7b326f67d..efeae536478 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/antidiagonal.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/antidiagonal.php @@ -16,7 +16,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function antidiagonal($matrix) +function antidiagonal($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/cofactors.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/cofactors.php index 07be8eeda63..5b3ce93b719 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/cofactors.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/cofactors.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function cofactors($matrix) +function cofactors($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/determinant.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/determinant.php index edadb3108a6..cda4144a669 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/determinant.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/determinant.php @@ -17,7 +17,7 @@ namespace Matrix; * @return float Matrix determinant * @throws Exception If argument isn't a valid matrix or array. */ -function determinant($matrix) +function determinant($matrix): float { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/diagonal.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/diagonal.php index 0bf05c9136a..be536c3c235 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/diagonal.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/diagonal.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function diagonal($matrix) +function diagonal($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/identity.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/identity.php index 0a52db2b66c..a261e28f8be 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/identity.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/identity.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The identity matrix * @throws Exception If argument isn't a valid matrix or array. */ -function identity($matrix) +function identity($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/inverse.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/inverse.php index d7cb5a073a0..82ff6d40d61 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/inverse.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/inverse.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function inverse($matrix) +function inverse($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/minors.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/minors.php index 9d0e4f52ce6..fc798b6ba60 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/minors.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/minors.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The new matrix * @throws Exception If argument isn't a valid matrix or array. */ -function minors($matrix) +function minors($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/trace.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/trace.php index 4031d5ed938..6f9df903dcd 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/trace.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/trace.php @@ -17,7 +17,7 @@ namespace Matrix; * @return float The trace of the matrix * @throws Exception If argument isn't a valid matrix or array. */ -function trace($matrix) +function trace($matrix): float { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/transpose.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/transpose.php index cfb021e210e..7daebee73d1 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/transpose.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/functions/transpose.php @@ -17,7 +17,7 @@ namespace Matrix; * @return Matrix The transposed matrix * @throws Exception If argument isn't a valid matrix or array. */ -function transpose($matrix) +function transpose($matrix): Matrix { if (is_array($matrix)) { $matrix = new Matrix($matrix); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/add.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/add.php index 4ae731221aa..6ce150f30ae 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/add.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/add.php @@ -19,7 +19,7 @@ use Matrix\Operators\Addition; * @return Matrix * @throws Exception */ -function add(...$matrixValues) +function add(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('Addition operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php index 9d15b89bb7d..0fb540d7ff0 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/directsum.php @@ -19,7 +19,7 @@ use Matrix\Operators\DirectSum; * @return Matrix * @throws Exception */ -function directsum(...$matrixValues) +function directsum(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('DirectSum operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php index 767d03a5af5..3c6074bb069 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideby.php @@ -19,7 +19,7 @@ use Matrix\Operators\Division; * @return Matrix * @throws Exception */ -function divideby(...$matrixValues) +function divideby(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('Division operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php index f5cb8dca1e1..d0487c8e8a5 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/divideinto.php @@ -19,7 +19,7 @@ use Matrix\Operators\Division; * @return Matrix * @throws Exception */ -function divideinto(...$matrixValues) +function divideinto(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('Division operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php index 1428f091fb4..10bca05f35e 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/multiply.php @@ -19,7 +19,7 @@ use Matrix\Operators\Multiplication; * @return Matrix * @throws Exception */ -function multiply(...$matrixValues) +function multiply(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('Multiplication operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php index 3123b61ec8a..55a827f6844 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/classes/src/operations/subtract.php @@ -19,7 +19,7 @@ use Matrix\Operators\Subtraction; * @return Matrix * @throws Exception */ -function subtract(...$matrixValues) +function subtract(...$matrixValues): Matrix { if (count($matrixValues) < 2) { throw new Exception('Subtraction operation requires at least 2 arguments'); diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.7.2.json b/lib/phpspreadsheet/vendor/markbaker/matrix/composer.7.2.json deleted file mode 100644 index 6ff324d00b7..00000000000 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.7.2.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "name": "markbaker/matrix", - "type": "library", - "description": "PHP Class for working with matrices", - "keywords": ["matrix", "vector", "mathematics"], - "homepage": "https://github.com/MarkBaker/PHPMatrix", - "license": "MIT", - "authors": [ - { - "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" - } - ], - "require": { - "php": "^7.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.4@dev", - "squizlabs/php_codesniffer": "^3.0@dev", - "phpmd/phpmd": "dev-master", - "infection/infection": "0.13.x-dev", - "phpstan/phpstan": "^0.12.0@dev", - "sebastian/phpcpd": "^4.1", - "phploc/phploc": "^5.0@dev", - "phpcompatibility/php-compatibility": "dev-master", - "dealerdirect/phpcodesniffer-composer-installer": "dev-master" - }, - "autoload": { - "psr-4": { - "Matrix\\": "classes/src/" - }, - "files": [ - "classes/src/functions/adjoint.php", - "classes/src/functions/antidiagonal.php", - "classes/src/functions/cofactors.php", - "classes/src/functions/determinant.php", - "classes/src/functions/diagonal.php", - "classes/src/functions/identity.php", - "classes/src/functions/inverse.php", - "classes/src/functions/minors.php", - "classes/src/functions/trace.php", - "classes/src/functions/transpose.php", - "classes/src/operations/add.php", - "classes/src/operations/directsum.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Matrix\\Test\\": "unitTests/classes/src/" - }, - "files": [ - "unitTests/classes/src/functions/adjointTest.php", - "unitTests/classes/src/functions/antidiagonalTest.php", - "unitTests/classes/src/functions/cofactorsTest.php", - "unitTests/classes/src/functions/determinantTest.php", - "unitTests/classes/src/functions/diagonalTest.php", - "unitTests/classes/src/functions/identityTest.php", - "unitTests/classes/src/functions/inverseTest.php", - "unitTests/classes/src/functions/minorsTest.php", - "unitTests/classes/src/functions/traceTest.php", - "unitTests/classes/src/functions/transposeTest.php", - "unitTests/classes/src/operations/addTest.php", - "unitTests/classes/src/operations/directsumTest.php", - "unitTests/classes/src/operations/subtractTest.php", - "unitTests/classes/src/operations/multiplyTest.php", - "unitTests/classes/src/operations/dividebyTest.php", - "unitTests/classes/src/operations/divideintoTest.php" - ] - }, - "scripts": { - "style": "phpcs --report-width=200 --report=summary,full -n", - "test": "phpunit -c phpunit.xml.dist", - "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n", - "lines": "phploc classes/src/ -n", - "cpd": "phpcpd classes/src/ -n", - "versions": "phpcs --report-width=200 --report=summary,full classes/src/ --standard=PHPCompatibility --runtime-set testVersion 5.6- -n", - "infection": "infection --min-msi=70 --min-covered-msi=70 --log-verbosity=all", - "phpstan": "phpstan analyse classes/src/ -c phpstan.neon --level=7 --no-progress -vvv --memory-limit=1024M", - "coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage" - }, - "minimum-stability": "dev" -} diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json b/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json index dae5c98c05b..1386afc2aa3 100644 --- a/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/composer.json @@ -8,20 +8,21 @@ "authors": [ { "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" + "email": "mark@demon-angel.eu" } ], "require": { - "php": "^5.6.0|^7.0.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7", - "phpmd/phpmd": "dev-master", - "sebastian/phpcpd": "^3.0", - "phploc/phploc": "^4", - "squizlabs/php_codesniffer": "^3.0@dev", - "phpcompatibility/php-compatibility": "dev-master", - "dealerdirect/phpcodesniffer-composer-installer": "dev-master" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phpmd/phpmd": "2.*", + "sebastian/phpcpd": "^4.0", + "phploc/phploc": "^4.0", + "squizlabs/php_codesniffer": "^3.4", + "phpcompatibility/php-compatibility": "^9.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0" }, "autoload": { "psr-4": { @@ -70,12 +71,13 @@ ] }, "scripts": { - "style": "phpcs --report-width=200 --report=summary,full -n", + "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n", "test": "phpunit -c phpunit.xml.dist", "mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n", "lines": "phploc classes/src/ -n", "cpd": "phpcpd classes/src/ -n", - "versions": "phpcs --report-width=200 --report=summary,full classes/src/ --standard=PHPCompatibility --runtime-set testVersion 5.6- -n" + "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n", + "coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage" }, "minimum-stability": "dev" } diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/infection.json.dist b/lib/phpspreadsheet/vendor/markbaker/matrix/infection.json.dist new file mode 100644 index 00000000000..eddaa70a2ef --- /dev/null +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/infection.json.dist @@ -0,0 +1,17 @@ +{ + "timeout": 1, + "source": { + "directories": [ + "classes\/src" + ] + }, + "logs": { + "text": "build/infection/text.log", + "summary": "build/infection/summary.log", + "debug": "build/infection/debug.log", + "perMutator": "build/infection/perMutator.md" + }, + "mutators": { + "@default": true + } +} diff --git a/lib/phpspreadsheet/vendor/markbaker/matrix/phpstan.neon b/lib/phpspreadsheet/vendor/markbaker/matrix/phpstan.neon new file mode 100644 index 00000000000..1607abd7c0c --- /dev/null +++ b/lib/phpspreadsheet/vendor/markbaker/matrix/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + ignoreErrors: + - '#Property [A-Za-z\\]+::\$[A-Za-z]+ has no typehint specified#' + - '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has no return typehint specified#' + checkMissingIterableValueType: false diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/LICENSE b/lib/phpspreadsheet/vendor/myclabs/php-enum/LICENSE new file mode 100644 index 00000000000..2a8cf22ec64 --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2015 My C-Labs + +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/lib/phpspreadsheet/vendor/myclabs/php-enum/README.md b/lib/phpspreadsheet/vendor/myclabs/php-enum/README.md new file mode 100644 index 00000000000..bf1c91dd553 --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/README.md @@ -0,0 +1,134 @@ +# PHP Enum implementation inspired from SplEnum + +[![Build Status](https://travis-ci.org/myclabs/php-enum.png?branch=master)](https://travis-ci.org/myclabs/php-enum) +[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum) +[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum) +[![psalm](https://shepherd.dev/github/myclabs/php-enum/coverage.svg)](https://shepherd.dev/github/myclabs/php-enum) + +Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme). + +## Why? + +First, and mainly, `SplEnum` is not integrated to PHP, you have to install the extension separately. + +Using an enum instead of class constants provides the following advantages: + +- You can use an enum as a parameter type: `function setAction(Action $action) {` +- You can use an enum as a return type: `function getAction() : Action {` +- You can enrich the enum with methods (e.g. `format`, `parse`, …) +- You can extend the enum to add new values (make your enum `final` to prevent it) +- You can get a list of all the possible values (see below) + +This Enum class is not intended to replace class constants, but only to be used when it makes sense. + +## Installation + +``` +composer require myclabs/php-enum +``` + +## Declaration + +```php +use MyCLabs\Enum\Enum; + +/** + * Action enum + */ +class Action extends Enum +{ + private const VIEW = 'view'; + private const EDIT = 'edit'; +} +``` + +## Usage + +```php +$action = Action::VIEW(); + +// or with a dynamic key: +$action = Action::$key(); +// or with a dynamic value: +$action = new Action($value); +``` + +As you can see, static methods are automatically implemented to provide quick access to an enum value. + +One advantage over using class constants is to be able to use an enum as a parameter type: + +```php +function setAction(Action $action) { + // ... +} +``` + +## Documentation + +- `__construct()` The constructor checks that the value exist in the enum +- `__toString()` You can `echo $myValue`, it will display the enum value (value of the constant) +- `getValue()` Returns the current value of the enum +- `getKey()` Returns the key of the current value on Enum +- `equals()` Tests whether enum instances are equal (returns `true` if enum values are equal, `false` otherwise) + +Static methods: + +- `toArray()` method Returns all possible values as an array (constant name in key, constant value in value) +- `keys()` Returns the names (keys) of all constants in the Enum class +- `values()` Returns instances of the Enum class of all Enum constants (constant name in key, Enum instance in value) +- `isValid()` Check if tested value is valid on enum set +- `isValidKey()` Check if tested key is valid on enum set +- `search()` Return key for searched value + +### Static methods + +```php +class Action extends Enum +{ + private const VIEW = 'view'; + private const EDIT = 'edit'; +} + +// Static method: +$action = Action::VIEW(); +$action = Action::EDIT(); +``` + +Static method helpers are implemented using [`__callStatic()`](http://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic). + +If you care about IDE autocompletion, you can either implement the static methods yourself: + +```php +class Action extends Enum +{ + private const VIEW = 'view'; + + /** + * @return Action + */ + public static function VIEW() { + return new Action(self::VIEW); + } +} +``` + +or you can use phpdoc (this is supported in PhpStorm for example): + +```php +/** + * @method static Action VIEW() + * @method static Action EDIT() + */ +class Action extends Enum +{ + private const VIEW = 'view'; + private const EDIT = 'edit'; +} +``` + +## Related projects + +- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type) +- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter) +- [PHPStan integration](https://github.com/timeweb/phpstan-enum) +- [Yii2 enum mapping](https://github.com/KartaviK/yii2-enum) diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/SECURITY.md b/lib/phpspreadsheet/vendor/myclabs/php-enum/SECURITY.md new file mode 100644 index 00000000000..84fd4e32021 --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported Versions + +Only the latest stable release is supported. + +## Reporting a Vulnerability + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). + +Tidelift will coordinate the fix and disclosure. diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/composer.json b/lib/phpspreadsheet/vendor/myclabs/php-enum/composer.json new file mode 100644 index 00000000000..6861a5ce5fb --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/composer.json @@ -0,0 +1,33 @@ +{ + "name": "myclabs/php-enum", + "type": "library", + "description": "PHP Enum implementation", + "keywords": ["enum"], + "homepage": "http://github.com/myclabs/php-enum", + "license": "MIT", + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "MyCLabs\\Tests\\Enum\\": "tests/" + } + }, + "require": { + "php": ">=7.1", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "^7", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^3.8" + } +} diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/psalm.xml b/lib/phpspreadsheet/vendor/myclabs/php-enum/psalm.xml new file mode 100644 index 00000000000..b07e9294a43 --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/psalm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/src/Enum.php b/lib/phpspreadsheet/vendor/myclabs/php-enum/src/Enum.php new file mode 100644 index 00000000000..b8b93277e2c --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/src/Enum.php @@ -0,0 +1,250 @@ + + * @author Daniel Costa + * @author Mirosław Filip + * + * @psalm-template T + * @psalm-immutable + */ +abstract class Enum implements \JsonSerializable +{ + /** + * Enum value + * + * @var mixed + * @psalm-var T + */ + protected $value; + + /** + * Store existing constants in a static cache per object. + * + * + * @var array + * @psalm-var array> + */ + protected static $cache = []; + + /** + * Cache of instances of the Enum class + * + * @var array + * @psalm-var array> + */ + protected static $instances = []; + + /** + * Creates a new value of some type + * + * @psalm-pure + * @param mixed $value + * + * @psalm-param static|T $value + * @throws \UnexpectedValueException if incompatible type is given. + */ + public function __construct($value) + { + if ($value instanceof static) { + /** @psalm-var T */ + $value = $value->getValue(); + } + + if (!$this->isValid($value)) { + /** @psalm-suppress InvalidCast */ + throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); + } + + /** @psalm-var T */ + $this->value = $value; + } + + /** + * @psalm-pure + * @return mixed + * @psalm-return T + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the enum key (i.e. the constant name). + * + * @psalm-pure + * @return mixed + */ + public function getKey() + { + return static::search($this->value); + } + + /** + * @psalm-pure + * @psalm-suppress InvalidCast + * @return string + */ + public function __toString() + { + return (string)$this->value; + } + + /** + * Determines if Enum should be considered equal with the variable passed as a parameter. + * Returns false if an argument is an object of different class or not an object. + * + * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4 + * + * @psalm-pure + * @psalm-param mixed $variable + * @return bool + */ + final public function equals($variable = null): bool + { + return $variable instanceof self + && $this->getValue() === $variable->getValue() + && static::class === \get_class($variable); + } + + /** + * Returns the names (keys) of all constants in the Enum class + * + * @psalm-pure + * @psalm-return list + * @return array + */ + public static function keys() + { + return \array_keys(static::toArray()); + } + + /** + * Returns instances of the Enum class of all Enum constants + * + * @psalm-pure + * @psalm-return array + * @return static[] Constant name in key, Enum instance in value + */ + public static function values() + { + $values = array(); + + /** @psalm-var T $value */ + foreach (static::toArray() as $key => $value) { + $values[$key] = new static($value); + } + + return $values; + } + + /** + * Returns all possible values as an array + * + * @psalm-pure + * @psalm-suppress ImpureStaticProperty + * + * @psalm-return array + * @return array Constant name in key, constant value in value + */ + public static function toArray() + { + $class = static::class; + + if (!isset(static::$cache[$class])) { + $reflection = new \ReflectionClass($class); + static::$cache[$class] = $reflection->getConstants(); + } + + return static::$cache[$class]; + } + + /** + * Check if is valid enum value + * + * @param $value + * @psalm-param mixed $value + * @psalm-pure + * @return bool + */ + public static function isValid($value) + { + return \in_array($value, static::toArray(), true); + } + + /** + * Check if is valid enum key + * + * @param $key + * @psalm-param string $key + * @psalm-pure + * @return bool + */ + public static function isValidKey($key) + { + $array = static::toArray(); + + return isset($array[$key]) || \array_key_exists($key, $array); + } + + /** + * Return key for value + * + * @param $value + * + * @psalm-param mixed $value + * @psalm-pure + * @return mixed + */ + public static function search($value) + { + return \array_search($value, static::toArray(), true); + } + + /** + * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant + * + * @param string $name + * @param array $arguments + * + * @return static + * @throws \BadMethodCallException + */ + public static function __callStatic($name, $arguments) + { + $class = static::class; + if (!isset(self::$instances[$class][$name])) { + $array = static::toArray(); + if (!isset($array[$name]) && !\array_key_exists($name, $array)) { + $message = "No static method or enum constant '$name' in class " . static::class; + throw new \BadMethodCallException($message); + } + return self::$instances[$class][$name] = new static($array[$name]); + } + return clone self::$instances[$class][$name]; + } + + /** + * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode() + * natively. + * + * @return mixed + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @psalm-pure + */ + public function jsonSerialize() + { + return $this->getValue(); + } +} diff --git a/lib/phpspreadsheet/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php b/lib/phpspreadsheet/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php new file mode 100644 index 00000000000..302bf80ebf5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php @@ -0,0 +1,54 @@ +register(new \MyCLabs\Enum\PHPUnit\Comparator()); + */ +final class Comparator extends \SebastianBergmann\Comparator\Comparator +{ + public function accepts($expected, $actual) + { + return $expected instanceof Enum && ( + $actual instanceof Enum || $actual === null + ); + } + + /** + * @param Enum $expected + * @param Enum|null $actual + * + * @return void + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($expected->equals($actual)) { + return; + } + + throw new ComparisonFailure( + $expected, + $actual, + $this->formatEnum($expected), + $this->formatEnum($actual), + false, + 'Failed asserting that two Enums are equal.' + ); + } + + private function formatEnum(Enum $enum = null) + { + if ($enum === null) { + return "null"; + } + + return get_class($enum)."::{$enum->getKey()}()"; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist new file mode 100644 index 00000000000..3eafb6ca19f --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist @@ -0,0 +1,22 @@ + + + + samples + src + tests + + samples/Header.php + */tests/Core/*/*Test\.(inc|css|js)$ + + + + + + + + + + + + diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md deleted file mode 100644 index 3c299020ed0..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md +++ /dev/null @@ -1,1593 +0,0 @@ -# Changelog for PHPExcel - -This is the historic changelog of the project when it was still called PHPExcel. -It exists only for historical purposes and versions mentioned here should not be -confused with PhpSpreadsheet versions. - -## [1.8.1] - 2015-04-30 - -### Bugfixes - -- Fix for Writing an Open Document cell with non-numeric formula - @goncons [#397](https://github.com/PHPOffice/PHPExcel/issues/397) -- Avoid potential divide by zero in basedrawing - @sarciszewski [#329](https://github.com/PHPOffice/PHPExcel/issues/329) -- XML External Entity (XXE) Processing, different behaviour between simplexml_load_string() and simplexml_load_file(). - @ymaerschalck [#405](https://github.com/PHPOffice/PHPExcel/issues/405) -- Fix to ensure that current cell is maintained when executing formula calculations - @MarkBaker -- Keep/set the value on Reader _loadSheetsOnly as NULL, courtesy of Restless-ET - @MarkBaker [#350](https://github.com/PHPOffice/PHPExcel/issues/350) -- Loading an Excel 2007 spreadsheet throws an "Autofilter must be set on a range of cells" exception - @MarkBaker [CodePlex #18105](https://phpexcel.codeplex.com/workitem/18105) -- Fix to autoloader registration for backward compatibility with PHP 5.2.0 not accepting the prepend flag - @MarkBaker [#388](https://github.com/PHPOffice/PHPExcel/issues/388) -- DOM loadHTMLFile() failing with options flags when using PHP < 5.4.0 - @MarkBaker [#384](https://github.com/PHPOffice/PHPExcel/issues/384) -- Fix for percentage operator in formulae for BIFF Writer - @MarkBaker -- Fix to getStyle() call for cell object - @MarkBaker -- Discard Autofilters in Excel2007 Reader when filter range isn't a valid range - @MarkBaker -- Fix invalid NA return in VLOOKUP - @frozenstupidity [#423](https://github.com/PHPOffice/PHPExcel/issues/423) -- "No Impact" conditional formatting fix for NumberFormat - @wiseloren [CodePlex #21454](https://phpexcel.codeplex.com/workitem/21454) -- Bug in Excel2003XML reader, parsing merged cells - @bobwitlox [#467](https://github.com/PHPOffice/PHPExcel/issues/467) -- Fix for CEIL() and FLOOR() when number argument is zero - @MarkBaker [#302](https://github.com/PHPOffice/PHPExcel/issues/302) - -### General - -- Remove cells cleanly when calling RemoveRow() or RemoveColumn() - @MarkBaker -- Small performance improvement for autosize columns - @MarkBaker -- Change the getter/setter for zeroHeight to camel case - @frost-nzcr4 [#379](https://github.com/PHPOffice/PHPExcel/issues/379) -- DefaultValueBinder is too much aggressive when converting string to numeric - @MarkBaker [#394](https://github.com/PHPOffice/PHPExcel/issues/394) -- Default precalculate formulas to false for writers - @MarkBaker -- Set default Cyclic Reference behaviour to 1 to eliminate exception when using a single cyclic iteration in formulae - @MarkBaker - -### Features - -- Some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE - @MarkBaker [#396](https://github.com/PHPOffice/PHPExcel/issues/396) -- Methods to manage most of the existing options for Chart Axis, Major Grid-lines and Minor Grid-lines - @WiktrzGE [#404](https://github.com/PHPOffice/PHPExcel/issues/404) -- ODS read/write comments in the cell - @frost-nzcr4 [#403](https://github.com/PHPOffice/PHPExcel/issues/403) -- Additional Mac CJK codepage definitions - @CQD [#389](https://github.com/PHPOffice/PHPExcel/issues/389) -- Update Worksheet.php getStyleByColumnAndRow() to allow a range of cells rather than just a single cell - @bolovincev [#269](https://github.com/PHPOffice/PHPExcel/issues/269) -- New methods added for testing cell status within merge groups - @MarkBaker -- Handling merge cells in HTML Reader - @cifren/MBaker [#205](https://github.com/PHPOffice/PHPExcel/issues/205) -- Helper to convert basic HTML markup to a Rich Text object - @MarkBaker -- Improved Iterators - @MarkBaker - - New Column Iterator - - Support for row and column ranges - - Improved handling for next/prev - -### Security - -- XML filescan in XML-based Readers to prevent XML Entity Expansion (XEE) - @MarkBaker - - (see http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion for an explanation of XEE injection) attacks - - Reference CVE-2015-3542 - Identification of problem courtesy of Dawid Golunski (Pentest Ltd.) - -## [1.8.0] - 2014-03-02 - -### Bugfixes - -- Undefined variable: fileHandle in CSV Reader - @MarkBaker [CodePlex #19830](https://phpexcel.codeplex.com/workitem/19830) -- Out of memory in style/supervisor.php - @MarkBaker [CodePlex #19968](https://phpexcel.codeplex.com/workitem/19968) -- Style error with merged cells in PDF Writer - @MarkBaker -- Problem with cloning worksheets - @MarkBaker -- Bug fix reading Open Office files - @tavoarcila [#259](https://github.com/PHPOffice/PHPExcel/issues/259) -- Serious bug in absolute cell reference used in shared formula - @MarkBaker [CodePlex #20397](https://phpexcel.codeplex.com/workitem/20397) - - Would also have affected insert/delete column/row- CHOOSE() returns "#VALUE!" if the 1st entry is chosen - @RomanSyroeshko [#267](https://github.com/PHPOffice/PHPExcel/issues/267) -- When duplicating styles, styles shifted by one column to the right - @Gemorroj [#268](https://github.com/PHPOffice/PHPExcel/issues/268) - - Fix also applied to duplicating conditional styles- Fix for formulae that reference a sheet whose name begins with a digit: - @IndrekHaav [#212](https://github.com/PHPOffice/PHPExcel/issues/212) - - these were erroneously identified as numeric values, causing the parser to throw an undefined variable error.- Fixed undefined variable error due to $styleArray being used before it's initialised - @IndrekHaav [CodePlex #16208](https://phpexcel.codeplex.com/workitem/16208) -- ISTEXT() return wrong result if referencing an empty but formatted cell - @PowerKiKi [#273](https://github.com/PHPOffice/PHPExcel/issues/273) -- Binary comparison of strings are case insensitive - @PowerKiKi [#270](https://github.com/PHPOffice/PHPExcel/issues/270), [#31](https://github.com/PHPOffice/PHPExcel/issues/31) -- Insert New Row/Column Before is not correctly updating formula references - @MarkBaker [#275](https://github.com/PHPOffice/PHPExcel/issues/275) -- Passing an array of cells to _generateRow() in the HTML/PDF Writer causes caching problems with last cell in the range - @MarkBaker [#257](https://github.com/PHPOffice/PHPExcel/issues/257) -- Fix to empty worksheet garbage collection when using cell caching - @MarkBaker [#193](https://github.com/PHPOffice/PHPExcel/issues/193) -- Excel2007 does not correctly mark rows as hidden - @Jazzo [#248](https://github.com/PHPOffice/PHPExcel/issues/248) -- Fixed typo in Chart/Layout set/getYMode() - @Roy Shahbazian [#299](https://github.com/PHPOffice/PHPExcel/issues/299) -- Fatal error: Call to a member function cellExists() line: 3327 in calculation.php if referenced worksheet doesn't exist - @EliuFlorez [#279](https://github.com/PHPOffice/PHPExcel/issues/279) -- AdvancedValueBinder "Division by zero"-error - @MarkBaker [#290](https://github.com/PHPOffice/PHPExcel/issues/290) -- Adding Sheet to Workbook Bug - @MarkBaker [CodePlex #20604](https://phpexcel.codeplex.com/workitem/20604) -- Calculation engine incorrectly evaluates empty cells as #VALUE - @MarkBaker [CodePlex #20703](https://phpexcel.codeplex.com/workitem/20703) -- Formula references to cell on another sheet in ODS files - @MarkBaker [CodePlex #20760](https://phpexcel.codeplex.com/workitem/20760) - -### Features - -- LibreOffice created XLSX files results in an empty file. - @MarkBaker [#321](https://github.com/PHPOffice/PHPExcel/issues/321), [#158](https://github.com/PHPOffice/PHPExcel/issues/158), [CodePlex #17824](https://phpexcel.codeplex.com/workitem/17824) -- Implementation of the Excel HLOOKUP() function - @amerov -- Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - @MarkBaker -- Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer - @MarkBaker -- Add support for reading protected (RC4 encrypted) .xls files - @trvrnrth [#261](https://github.com/PHPOffice/PHPExcel/issues/261) - -### General - -- Adding support for macros, Ribbon in Excel 2007 - @LWol [#252](https://github.com/PHPOffice/PHPExcel/issues/252) -- Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - @cdhutch [CodePlex #20055](https://phpexcel.codeplex.com/workitem/20055) -- Improve stock chart handling and rendering, with help from Swashata Ghosh - @MarkBaker -- Fix to calculation properties for Excel2007 so that the opening application will only recalculate on load if it's actually required - @MarkBaker -- Modified Excel2007 Writer to default preCalculateFormulas to false - @MarkBaker - - Note that autosize columns will still recalculate affected formulae internally- Functionality to getHighestRow() for a specified column, and getHighestColumn() for a specified row - @dresenhista [#242](https://github.com/PHPOffice/PHPExcel/issues/242) -- Modify PHPExcel_Reader_Excel2007 to use zipClass from PHPExcel_Settings::getZipClass() - @adamriyadi [#247](https://github.com/PHPOffice/PHPExcel/issues/247) - - This allows the use of PCLZip when reading for people that don't have access to ZipArchive -### Security - -- Convert properties to string in OOCalc reader - @infojunkie [#276](https://github.com/PHPOffice/PHPExcel/issues/276) -- Disable libxml external entity loading by default. - @maartenba [#322](https://github.com/PHPOffice/PHPExcel/issues/322) - - This is to prevent XML External Entity Processing (XXE) injection attacks (see https://websec.io/2012/08/27/Preventing-XEE-in-PHP.html for an explanation of XXE injection). - - Reference CVE-2014-2054 - -## [1.7.9] - 2013-06-02 - -### Features - -- Include charts option for HTML Writer - @MarkBaker -- Added composer file - @MarkBaker -- cache_in_memory_gzip "eats" last worksheet line, cache_in_memory doesn't - @MarkBaker [CodePlex #18844](https://phpexcel.codeplex.com/workitem/18844) -- echo statements in HTML.php - @MarkBaker [#104](https://github.com/PHPOffice/PHPExcel/issues/104) - -### Bugfixes - -- Added getStyle() method to Cell object - @MarkBaker -- Error in PHPEXCEL/Calculation.php script on line 2976 (stack pop check) - @Asker [CodePlex #18777](https://phpexcel.codeplex.com/workitem/18777) -- CSV files without a file extension being identified as HTML - @MarkBaker [CodePlex #18794](https://phpexcel.codeplex.com/workitem/18794) -- Wrong check for maximum number of rows in Excel5 Writer - @AndreKR [#66](https://github.com/PHPOffice/PHPExcel/issues/66) -- Cache directory for DiscISAM cache storage cannot be set - @MarkBaker [#67](https://github.com/PHPOffice/PHPExcel/issues/67) -- Fix to Excel2007 Reader for hyperlinks with an anchor fragment (following a #), otherwise they were treated as sheet references - @MarkBaker [CodePlex #17976](https://phpexcel.codeplex.com/workitem/17976) -- getSheetNames() fails on numeric (floating point style) names with trailing zeroes - @MarkBaker [CodePlex #18963](https://phpexcel.codeplex.com/workitem/18963) -- Modify cell's getCalculatedValue() method to return the content of RichText objects rather than the RichText object itself - @MarkBaker -- Fixed formula/formatting bug when removing rows - @techhead [#70](https://github.com/PHPOffice/PHPExcel/issues/70) -- Fix to cellExists for non-existent namedRanges - @alexgann [#63](https://github.com/PHPOffice/PHPExcel/issues/63) -- Sheet View in Excel5 Writer - @Progi1984 [#22](https://github.com/PHPOffice/PHPExcel/issues/22) -- PHPExcel_Worksheet::getCellCollection() may not return last cached cell - @amironov [#82](https://github.com/PHPOffice/PHPExcel/issues/82) -- Rich Text containing UTF-8 characters creating unreadable content with Excel5 Writer - @teso [CodePlex #18551](https://phpexcel.codeplex.com/workitem/18551) -- Work item GH-8/CP11704 : Conditional formatting in Excel 5 Writer - @Progi1984 -- canRead() Error for GoogleDocs ODS files: in ODS files from Google Docs there is no mimetype file - @MarkBaker [#113](https://github.com/PHPOffice/PHPExcel/issues/113) -- "Sheet index is out of bounds." Exception - @MarkBaker [#80](https://github.com/PHPOffice/PHPExcel/issues/80) -- Fixed number format fatal error - @ccorliss [#105](https://github.com/PHPOffice/PHPExcel/issues/105) -- Add DROP TABLE in destructor for SQLite and SQLite3 cache controllers - @MarkBaker -- Fix merged-cell borders on HTML/PDF output - @alexgann [#154](https://github.com/PHPOffice/PHPExcel/issues/154) -- Fix: Hyperlinks break when removing rows - @Shanto [#161](https://github.com/PHPOffice/PHPExcel/issues/161) -- Fix Extra Table Row From Images and Charts - @neclimdul [#166](https://github.com/PHPOffice/PHPExcel/issues/166) - -### General - -- Single cell print area - @MarkBaker [#130](https://github.com/PHPOffice/PHPExcel/issues/130) -- Improved AdvancedValueBinder for currency - @kea [#69](https://github.com/PHPOffice/PHPExcel/issues/69) -- Fix for environments where there is no access to /tmp but to upload_tmp_dir - @MarkBaker - - Provided an option to set the sys_get_temp_dir() call to use the upload_tmp_dir; though by default the standard temp directory will still be used- Search style by identity in PHPExcel_Worksheet::duplicateStyle() - @amironov [#84](https://github.com/PHPOffice/PHPExcel/issues/84) -- Fill SheetView IO in Excel5 - @karak [#85](https://github.com/PHPOffice/PHPExcel/issues/85) -- Memory and Speed improvements in PHPExcel_Reader_Excel5 - @cfhay [CodePlex #18958](https://phpexcel.codeplex.com/workitem/18958) -- Modify listWorksheetNames() and listWorksheetInfo to use XMLReader with streamed XML rather than SimpleXML - @MarkBaker [#78](https://github.com/PHPOffice/PHPExcel/issues/78) -- Restructuring of PHPExcel Exceptions - @dbonsch -- Refactor Calculation Engine from singleton to a Multiton - @MarkBaker - - Ensures that calculation cache is maintained independently for different workbooks - -## [1.7.8] - 2012-10-12 - -### Features - -- Phar builder script to add phar file as a distribution option - @kkamkou -- Refactor PDF Writer to allow use with a choice of PDF Rendering library - @MarkBaker - - rather than restricting to tcPDF - - Current options are tcPDF, mPDF, DomPDF - - tcPDF Library has now been removed from the deployment bundle- Initial version of HTML Reader - @MarkBaker -- Implement support for AutoFilter in PHPExcel_Writer_Excel5 - @Progi1984 -- Modified ERF and ERFC Engineering functions to accept Excel 2010's modified acceptance of negative arguments - @MarkBaker -- Support SheetView `view` attribute (Excel2007) - @k1LoW -- Excel compatibility option added for writing CSV files - @MarkBaker - - While Excel 2010 can read CSV files with a simple UTF-8 BOM, Excel2007 and earlier require UTF-16LE encoded tab-separated files. - - The new setExcelCompatibility(TRUE) option for the CSV Writer will generate files with this formatting for easy import into Excel2007 and below.- Language implementations for Turkish (tr) - @MarkBaker -- Added fraction tests to advanced value binder - @MarkBaker - -### General - -- Allow call to font setUnderline() for underline format to specify a simple boolean for UNDERLINE_NONE or UNDERLINE_SINGLE - @MarkBaker -- Add Currency detection to the Advanced Value Binder - @alexgann -- setCellValueExplicitByColumnAndRow() do not return PHPExcel_Worksheet - @MarkBaker [CodePlex #18404](https://phpexcel.codeplex.com/workitem/18404) -- Reader factory doesn't read anymore XLTX and XLT files - @MarkBaker [CodePlex #18324](https://phpexcel.codeplex.com/workitem/18324) -- Magic __toString() method added to Cell object to return raw data value as a string - @MarkBaker -- Add cell indent to html rendering - @alexgann - -### Bugfixes - -- ZeroHeight for rows in sheet format - @Raghav1981 -- OOCalc cells containing inside the tag - @cyberconte -- Fix to listWorksheetInfo() method for OOCalc Reader - @schir1964 -- Support for "e" (epoch) date format mask - @MarkBaker - - Rendered as a 4-digit CE year in non-Excel outputs- Background color cell is always black when editing cell - @MarkBaker -- Allow "no impact" to formats on Conditional Formatting - @MarkBaker -- OOCalc Reader fix for NULL cells - @wackonline -- Fix to excel2007 Chart Writer when a $plotSeriesValues is empty - @seltzlab -- Various fixes to Chart handling - @MarkBaker -- Error loading xlsx file with column breaks - @MarkBaker [CodePlex #18370](https://phpexcel.codeplex.com/workitem/18370) -- OOCalc Reader now handles percentage and currency data types - @MarkBaker -- mb_stripos empty delimiter - @MarkBaker -- getNestingLevel() Error on Excel5 Read - @takaakik -- Fix to Excel5 Reader when cell annotations are defined before their referenced text objects - @MarkBaker -- OOCalc Reader modified to process number-rows-repeated - @MarkBaker -- Chart Title compatibility on Excel 2007 - @MarkBaker [CodePlex #18377](https://phpexcel.codeplex.com/workitem/18377) -- Chart Refresh returning cell reference rather than values - @MarkBaker [CodePlex #18146](https://phpexcel.codeplex.com/workitem/18146) -- Autoshape being identified in twoCellAnchor when includeCharts is TRUE triggering load error - @MarkBaker [CodePlex #18145](https://phpexcel.codeplex.com/workitem/18145) -- v-type texts for series labels now recognised and parsed correctly - @MarkBaker [CodePlex #18325](https://phpexcel.codeplex.com/workitem/18325) -- load file failed if the file has no extensionType - @wolf5x [CodePlex #18492](https://phpexcel.codeplex.com/workitem/18492) -- Pattern fill colours in Excel2007 Style Writer - @dverspui -- Excel2007 Writer order of font style elements to conform with Excel2003 using compatibility pack - @MarkBaker -- Problems with $_activeSheetIndex when decreased below 0. - @MarkBaker [CodePlex #18425](https://phpexcel.codeplex.com/workitem/18425) -- PHPExcel_CachedObjectStorage_SQLite3::cacheMethodIsAvailable() uses class_exists - autoloader throws error - @MarkBaker [CodePlex #18597](https://phpexcel.codeplex.com/workitem/18597) -- Cannot access private property PHPExcel_CachedObjectStorageFactory::$_cacheStorageMethod - @MarkBaker [CodePlex #18598](https://phpexcel.codeplex.com/workitem/18598) -- Data titles for charts - @MarkBaker [CodePlex #18397](https://phpexcel.codeplex.com/workitem/18397) - - PHPExcel_Chart_Layout now has methods for getting/setting switches for displaying/hiding chart data labels- Discard single cell merge ranges when reading (stupid that Excel allows them in the first place) - @MarkBaker -- Discard hidden autoFilter named ranges - @MarkBaker - -## [1.7.7] - 2012-05-19 - -### Bugfixes - -- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) -- Change iterators to implement Iterator rather than extend CachingIterator, as a fix for PHP 5.4. changes in SPL - @MarkBaker -- Invalid cell coordinate in Autofilter for Excel2007 Writer - @MarkBaker [CodePlex #15459](https://phpexcel.codeplex.com/workitem/15459) -- PCLZip library issue - @MarkBaker [CodePlex #15518](https://phpexcel.codeplex.com/workitem/15518) -- Excel2007 Reader canRead function bug - @MarkBaker [CodePlex #15537](https://phpexcel.codeplex.com/workitem/15537) -- Support for Excel functions whose return can be used as either a value or as a cell reference depending on its context within a formula - @MarkBaker -- ini_set() call in Calculation class destructor - @gilles06 [CodePlex #15707](https://phpexcel.codeplex.com/workitem/15707) -- RangeToArray strange array keys - @MarkBaker [CodePlex #15786](https://phpexcel.codeplex.com/workitem/15786) -- INDIRECT() function doesn't work with named ranges - @MarkBaker [CodePlex #15762](https://phpexcel.codeplex.com/workitem/15762) -- Locale-specific fix to text functions when passing a boolean argument instead of a string - @MarkBaker -- reader/CSV fails on this file - @MarkBaker [CodePlex #16246](https://phpexcel.codeplex.com/workitem/16246) - - auto_detect_line_endings now set in CSV reader- $arguments improperly used in CachedObjectStorage/PHPTemp.php - @MarkBaker [CodePlex #16212](https://phpexcel.codeplex.com/workitem/16212) -- Bug In Cache System (cell reference when throwing caching errors) - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) -- PHP Invalid index notice on writing excel file when active sheet has been deleted - @MarkBaker [CodePlex #16895](https://phpexcel.codeplex.com/workitem/16895) -- External links in Excel2010 files cause Fatal error - @MarkBaker [CodePlex #16956](https://phpexcel.codeplex.com/workitem/16956) -- Previous calculation engine error conditions trigger cyclic reference errors - @MarkBaker [CodePlex #16960](https://phpexcel.codeplex.com/workitem/16960) -- PHPExcel_Style::applyFromArray() returns null rather than style object in advanced mode - @mkopinsky [CodePlex #16266](https://phpexcel.codeplex.com/workitem/16266) -- Cell::getFormattedValue returns RichText object instead of string - @fauvel [CodePlex #16958](https://phpexcel.codeplex.com/workitem/16958) -- Indexed colors do not refer to Excel's indexed colors? - @MarkBaker [CodePlex #17166](https://phpexcel.codeplex.com/workitem/17166) -- Indexed colors should be consistent with Excel and start from 1 (current index starts at 0) - @MarkBaker [CodePlex #17199](https://phpexcel.codeplex.com/workitem/17199) -- Named Range definition in .xls when sheet reeference is quote wrapped - @MarkBaker [CodePlex #17262](https://phpexcel.codeplex.com/workitem/17262) -- duplicateStyle() method doesn't duplicate conditional formats - @MarkBaker [CodePlex #17403](https://phpexcel.codeplex.com/workitem/17403) - - Added an equivalent duplicateConditionalStyle() method for duplicating conditional styles- =sumproduct(A,B) <> =sumproduct(B,A) in xlsx - @bnr [CodePlex #17501](https://phpexcel.codeplex.com/workitem/17501) - -### Features - -- OOCalc cells contain same data bug? - @cyberconte [CodePlex #17471](https://phpexcel.codeplex.com/workitem/17471) -- listWorksheetInfo() method added to Readers... courtesy of Christopher Mullins - @schir1964 -- Options for cell caching using Igbinary and SQLite/SQlite3. - @MarkBaker -- Additional row iterator options: allow a start row to be defined in the constructor; seek(), and prev() methods added. - @MarkBaker -- Implement document properties in Excel5 writer - @Progi1984 [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) - -### General - -- Implement chart functionality (EXPERIMENTAL) - @MarkBaker [CodePlex #16](https://phpexcel.codeplex.com/workitem/16) - - Initial definition of chart objects. - - Reading Chart definitions through the Excel2007 Reader - - Facility to render charts to images using the 3rd-party jpgraph library - - Writing Charts using the Excel2007 Writer- Fix to build to ensure that Examples are included with the documentation - @MarkBaker -- Reduce cell caching overhead using dirty flag to ensure that cells are only rewritten to the cache if they have actually been changed - @MarkBaker -- Improved memory usage in CSV Writer - @MarkBaker -- Improved speed and memory usage in Excel5 Writer - @MarkBaker -- Experimental - @MarkBaker - - Added getHighestDataColumn(), getHighestDataRow(), getHighestRowAndColumn() and calculateWorksheetDataDimension() methods for the worksheet that return the highest row and column that have cell records- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916) -- Two easy to fix Issues concerning PHPExcel_Token_Stack (l10n/UC) - @MarkBaker [CodePlex #15405](https://phpexcel.codeplex.com/workitem/15405) -- Locale file paths not fit for windows - @MarkBaker [CodePlex #15461](https://phpexcel.codeplex.com/workitem/15461) -- Add file directory as a cache option for cache_to_discISAM - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643) -- Datatype.php & constant TYPE_NULL - @MarkBaker [CodePlex #16923](https://phpexcel.codeplex.com/workitem/16923) -- Ensure use of system temp directory for all temporary work files, unless explicitly specified - @MarkBaker -- [Patch] faster stringFromColumnIndex() - @char101 [CodePlex #16359](https://phpexcel.codeplex.com/workitem/16359) -- Fix for projects that still use old autoloaders - @whit1206 [CodePlex #16028](https://phpexcel.codeplex.com/workitem/16028) -- Unknown codepage: 10007 - @atz [CodePlex #17024](https://phpexcel.codeplex.com/workitem/17024) - - Additional Mac codepages - -## [1.7.6] - 2011-02-27 - -### Features - -- Provide option to use PCLZip as an alternative to ZipArchive. - @MarkBaker - - This allows the writing of Excel2007 files, even without ZipArchive enabled (it does require zlib), or when php_zip is one of the buggy PHP 5.2.6 or 5.2.8 versions - - It can be enabled using PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP); - - Note that it is not yet implemented as an alternative to ZipArchive for those Readers that are extracting from zips- Added listWorksheetNames() method to Readers that support multiple worksheets in a workbook, allowing a user to extract a list of all the worksheet names from a file without parsing/loading the whole file. - @MarkBaker [CodePlex #14979](https://phpexcel.codeplex.com/workitem/14979) -- Speed boost and memory reduction in the Worksheet toArray() method. - @MarkBaker -- Added new rangeToArray() and namedRangeToArray() methods to the PHPExcel_Worksheet object. - @MarkBaker - - Functionally, these are identical to the toArray() method, except that they take an additional first parameter of a Range (e.g. 'B2:C3') or a Named Range name. - - Modified the toArray() method so that it actually uses rangeToArray().- Added support for cell comments in the OOCalc, Gnumeric and Excel2003XML Readers, and in the Excel5 Reader - @MarkBaker -- Improved toFormattedString() handling for Currency and Accounting formats to render currency symbols - @MarkBaker - -### Bugfixes - -- Implement more Excel calculation functions - @MarkBaker - - Implemented the DAVERAGE(), DCOUNT(), DCOUNTA(), DGET(), DMAX(), DMIN(), DPRODUCT(), DSTDEV(), DSTDEVP(), DSUM(), DVAR() and DVARP() Database functions- Simple =IF() formula disappears - @MarkBaker [CodePlex #14888](https://phpexcel.codeplex.com/workitem/14888) -- PHP Warning: preg_match(): Compilation failed: PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X - @MarkBaker [CodePlex #14898](https://phpexcel.codeplex.com/workitem/14898) -- VLOOKUP choking on parameters in PHPExcel.1.7.5/PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #14901](https://phpexcel.codeplex.com/workitem/14901) -- PHPExcel_Cell::isInRange() incorrect results - offset by one column - @MarkBaker [CodePlex #14973](https://phpexcel.codeplex.com/workitem/14973) -- Treat CodePage of 0 as CP1251 (for .xls files written by applications that don't set the CodePage correctly, such as Apple Numbers) - @MarkBaker -- Need method for removing autoFilter - @MarkBaker [CodePlex #11583](https://phpexcel.codeplex.com/workitem/11583) -- coordinateFromString throws exception for rows greater than 99,999 - @MarkBaker [CodePlex #15029](https://phpexcel.codeplex.com/workitem/15029) -- PHPExcel Excel2007 Reader colour problems with solidfill - @MarkBaker [CodePlex #14999](https://phpexcel.codeplex.com/workitem/14999) -- Formatting get lost and edit a template XLSX file - @MarkBaker [CodePlex #13215](https://phpexcel.codeplex.com/workitem/13215) -- Excel 2007 Reader /writer lost fontcolor - @MarkBaker [CodePlex #14029](https://phpexcel.codeplex.com/workitem/14029) -- file that makes cells go black - @MarkBaker [CodePlex #13374](https://phpexcel.codeplex.com/workitem/13374) -- Minor patchfix for Excel2003XML Reader when XML is defined with a charset attribute - @MarkBaker -- PHPExcel_Worksheet->toArray() index problem - @MarkBaker [CodePlex #15089](https://phpexcel.codeplex.com/workitem/15089) -- Merge cells 'un-merge' when using an existing spreadsheet - @MarkBaker [CodePlex #15094](https://phpexcel.codeplex.com/workitem/15094) -- Worksheet fromArray() only working with 2-D arrays - @MarkBaker [CodePlex #15129](https://phpexcel.codeplex.com/workitem/15129) -- rangeToarray function modified for non-existent cells - @xkeshav [CodePlex #15172](https://phpexcel.codeplex.com/workitem/15172) -- Images not getting copyied with the ->clone function - @MarkBaker [CodePlex #14980](https://phpexcel.codeplex.com/workitem/14980) -- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @MarkBaker [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) -- Fix Excel5 Writer so that it only writes column dimensions for columns that are actually used rather than the full range (A to IV) - @MarkBaker -- FreezePane causing damaged or modified error - @MarkBaker [CodePlex #15198](https://phpexcel.codeplex.com/workitem/15198) - - The freezePaneByColumnAndRow() method row argument should default to 1 rather than 0. - - Default row argument for all __ByColumnAndRow() methods should be 1- Column reference rather than cell reference in Print Area definition - @MarkBaker [CodePlex #15121](https://phpexcel.codeplex.com/workitem/15121) - - Fix Excel2007 Writer to handle print areas that are defined as row or column ranges rather than just as cell ranges- Reduced false positives from isDateTimeFormatCode() method by suppressing testing within quoted strings - @MarkBaker -- Caching and tmp partition exhaustion - @MarkBaker [CodePlex #15312](https://phpexcel.codeplex.com/workitem/15312) -- Writing to Variable No Longer Works. $_tmp_dir Missing in PHPExcel\PHPExcel\Shared\OLE\PPS\Root.php - @MarkBaker [CodePlex #15308](https://phpexcel.codeplex.com/workitem/15308) -- Named ranges with dot don't get parsed properly - @MarkBaker [CodePlex #15379](https://phpexcel.codeplex.com/workitem/15379) -- insertNewRowBefore fails to consistently update references - @MarkBaker [CodePlex #15096](https://phpexcel.codeplex.com/workitem/15096) -- "i" is not a valid character for Excel date format masks (in isDateTimeFormatCode() method) - @MarkBaker -- PHPExcel_ReferenceHelper::insertNewBefore() is missing an 'Update worksheet: comments' section - @MKunert [CodePlex #15421](https://phpexcel.codeplex.com/workitem/15421) - -### General - -- Full column/row references in named ranges not supported by updateCellReference() - @MarkBaker [CodePlex #15409](https://phpexcel.codeplex.com/workitem/15409) -- Improved performance (speed), for building the Shared Strings table in the Excel2007 Writer. - @MarkBaker -- Improved performance (speed), for PHP to Excel date conversions - @MarkBaker -- Enhanced SheetViews element structures in the Excel2007 Writer for frozen panes. - @MarkBaker -- Removed Serialized Reader/Writer as these no longer work. - @MarkBaker - -## [1.7.5] - 2010-12-10 - -### Features - -- Implement Gnumeric File Format - @MarkBaker [CodePlex #8769](https://phpexcel.codeplex.com/workitem/8769) - - Initial work on Gnumeric Reader (Worksheet Data, Document Properties and basic Formatting)- Support for Extended Workbook Properties in Excel2007, Excel5 and OOCalc Readers; support for User-defined Workbook Properties in Excel2007 and OOCalc Readers - @MarkBaker -- Support for Extended and User-defined Workbook Properties in Excel2007 Writer - @MarkBaker -- Provided a setGenerateSheetNavigationBlock(false); option to suppress generation of the sheet navigation block when writing multiple worksheets to HTML - @MarkBaker -- Advanced Value Binder now recognises TRUE/FALSE strings (locale-specific) and converts to boolean - @MarkBaker -- PHPExcel_Worksheet->toArray() is returning truncated values - @MarkBaker [CodePlex #14301](https://phpexcel.codeplex.com/workitem/14301) -- Configure PDF Writer margins based on Excel Worksheet Margin Settings value - @MarkBaker -- Added Contiguous flag for the CSV Reader, when working with Read Filters - @MarkBaker -- Added getFormattedValue() method for cell object - @MarkBaker -- Added strictNullComparison argument to the worksheet fromArray() method - @MarkBaker - -### Bugfixes - -- Fix to toFormattedString() method in PHPExcel_Style_NumberFormat to handle fractions with a # code for the integer part - @MarkBaker -- NA() doesn't propagate in matrix calc - quick fix in JAMA/Matrix.php - @MarkBaker [CodePlex #14143](https://phpexcel.codeplex.com/workitem/14143) -- Excel5 : Formula : String constant containing double quotation mark - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Percent - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Error constant - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Concatenation operator - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Worksheet clone broken for CachedObjectStorage_Memory - @MarkBaker [CodePlex #14146](https://phpexcel.codeplex.com/workitem/14146) -- PHPExcel_Reader_Excel2007 fails when gradient fill without type is present in a file - @MarkBaker [CodePlex #12998](https://phpexcel.codeplex.com/workitem/12998) -- @ format for numeric strings in XLSX to CSV conversion - @MarkBaker [CodePlex #14176](https://phpexcel.codeplex.com/workitem/14176) -- Advanced Value Binder Not Working? - @MarkBaker [CodePlex #14223](https://phpexcel.codeplex.com/workitem/14223) -- unassigned object variable in PHPExcel->removeCellXfByIndex - @MarkBaker [CodePlex #14226](https://phpexcel.codeplex.com/workitem/14226) -- problem with getting cell values from another worksheet... (if cell doesn't exist) - @MarkBaker [CodePlex #14236](https://phpexcel.codeplex.com/workitem/14236) -- Setting cell values to one char strings & Trouble reading one character string (thanks gorfou) - @MarkBaker -- Worksheet title exception when duplicate worksheet is being renamed but exceeds the 31 character limit - @MarkBaker [CodePlex #14256](https://phpexcel.codeplex.com/workitem/14256) -- Named range with sheet name that contains the $ throws exception when getting the cell - @MarkBaker [CodePlex #14086](https://phpexcel.codeplex.com/workitem/14086) -- Added autoloader to DefaultValueBinder and AdvancedValueBinder - @MarkBaker -- Modified PHPExcel_Shared_Date::isDateTimeFormatCode() to return false if format code begins with "_" or with "0 " to prevent false positives - @MarkBaker - - These leading characters are most commonly associated with number, currency or accounting (or occasionally fraction) formats- BUG : Excel5 and setReadFilter ? - @MarkBaker [CodePlex #14374](https://phpexcel.codeplex.com/workitem/14374) -- Wrong exception message while deleting column - @MarkBaker [CodePlex #14425](https://phpexcel.codeplex.com/workitem/14425) -- Formula evaluation fails with Japanese sheet refs - @MarkBaker [CodePlex #14679](https://phpexcel.codeplex.com/workitem/14679) -- PHPExcel_Writer_PDF does not handle cell borders correctly - @MarkBaker [CodePlex #13559](https://phpexcel.codeplex.com/workitem/13559) -- Style : applyFromArray() for 'allborders' not working - @MarkBaker [CodePlex #14831](https://phpexcel.codeplex.com/workitem/14831) - -### General - -- Using $this when not in object context in Excel5 Reader - @MarkBaker [CodePlex #14837](https://phpexcel.codeplex.com/workitem/14837) -- Removes a unnecessary loop through each cell when applying conditional formatting to a range. - @MarkBaker -- Removed spurious PHP end tags (?>) - @MarkBaker -- Improved performance (speed) and reduced memory overheads, particularly for the Writers, but across the whole library. - @MarkBaker - -## [1.7.4] - 2010-08-26 - -### Bugfixes - -- Excel5 : Formula : Power - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Formula : Unary plus - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895) -- Excel5 : Just write the Escher stream if necessary in Worksheet - @Progi1984 -- Syntax errors in memcache.php 1.7.3c - @MarkBaker [CodePlex #13433](https://phpexcel.codeplex.com/workitem/13433) -- Support for row or column ranges in the calculation engine, e.g. =SUM(C:C) or =SUM(1:2) - @MarkBaker - - Also support in the calculation engine for absolute row or column ranges e.g. =SUM($C:$E) or =SUM($3:5)- Picture problem with Excel 2003 - @Erik Tilt [CodePlex #13455](https://phpexcel.codeplex.com/workitem/13455) -- Wrong variable used in addExternalSheet in PHPExcel.php - @MarkBaker [CodePlex #13484](https://phpexcel.codeplex.com/workitem/13484) -- "Invalid cell coordinate" error when formula access data from an other sheet - @MarkBaker [CodePlex #13515](https://phpexcel.codeplex.com/workitem/13515) -- (related to Work item 13515) Calculation engine confusing cell range worksheet when referencing cells in a different worksheet to the formula - @MarkBaker -- Wrong var naming in Worksheet->garbageCollect() - @MarkBaker [CodePlex #13752](https://phpexcel.codeplex.com/workitem/13752) -- PHPExcel_Style_*::__clone() methods cause cloning loops? - @MarkBaker [CodePlex #13764](https://phpexcel.codeplex.com/workitem/13764) -- Recent builds causing problems loading xlsx files? (ZipArchive issue?) - @MarkBaker [CodePlex #11488](https://phpexcel.codeplex.com/workitem/11488) -- cache_to_apc causes fatal error when processing large data sets - @MarkBaker [CodePlex #13856](https://phpexcel.codeplex.com/workitem/13856) -- OOCalc reader misses first line if it's a 'table-header-row' - @MarkBaker [CodePlex #13880](https://phpexcel.codeplex.com/workitem/13880) -- using cache with copy or clone bug? - @MarkBaker [CodePlex #14011](https://phpexcel.codeplex.com/workitem/14011) - - Fixed $worksheet->copy() or clone $worksheet when using cache_in_memory, cache_in_memory_gzip, cache_in_memory_serialized, cache_to_discISAM, cache_to_phpTemp, cache_to_apc and cache_to_memcache; - - Fixed but untested when using cache_to_wincache. -### Features - -- Standard Deviation functions returning DIV/0 Error when Standard Deviation is zero - @MarkBaker [CodePlex #13450](https://phpexcel.codeplex.com/workitem/13450) -- Support for print area with several ranges in the Excel2007 reader, and improved features for editing print area with several ranges - @MarkBaker -- Improved Cell Exception Reporting - @MarkBaker [CodePlex #13769](https://phpexcel.codeplex.com/workitem/13769) - -### General - -- Fixed problems with reading Excel2007 Properties - @MarkBaker -- PHP Strict Standards: Non-static method PHPExcel_Shared_String::utf16_decode() should not be called statically - @MarkBaker -- Array functions were ignored when loading an existing file containing them, and as a result, they would lose their 'cse' status. - @MarkBaker -- Minor memory tweaks to Excel2007 Writer - @MarkBaker -- Modified ReferenceHelper updateFormulaReferences() method to handle updates to row and column cell ranges (including absolute references e.g. =SUM(A:$E) or =SUM($5:5), and range/cell references that reference a worksheet by name), and to provide both performance and memory improvements. - @MarkBaker -- Modified Excel2007 Reader so that ReferenceHelper class is instantiated only once rather than for every shared formula in a workbook. - @MarkBaker -- Correct handling for additional (synonym) formula tokens in Excel5 Reader - @MarkBaker -- Additional reading of some Excel2007 Extended Properties (Company, Manager) - @MarkBaker - -## [1.7.3c] - 2010-06-01 - -### Bugfixes - -- Fatal error: Class 'ZipArchive' not found... ...Reader/Excel2007.php on line 217 - @MarkBaker [CodePlex #13012](https://phpexcel.codeplex.com/workitem/13012) -- PHPExcel_Writer_Excel2007 error after 1.7.3b - @MarkBaker [CodePlex #13398](https://phpexcel.codeplex.com/workitem/13398) - -## [1.7.3b] - 2010-05-31 - -### Bugfixes - -- Infinite loop when reading - @MarkBaker [CodePlex #12903](https://phpexcel.codeplex.com/workitem/12903) -- Wrong method chaining on PHPExcel_Worksheet class - @MarkBaker [CodePlex #13381](https://phpexcel.codeplex.com/workitem/13381) - -## [1.7.3] - 2010-05-17 - -### General - -- Applied patch 4990 (modified) - @Erik Tilt -- Applied patch 5568 (modified) - @MarkBaker -- Applied patch 5943 - @MarkBaker -- Upgrade build script to use Phing - @MarkBaker [CodePlex #13042](https://phpexcel.codeplex.com/workitem/13042) -- Replacing var with public/private - @Erik Tilt [CodePlex #11586](https://phpexcel.codeplex.com/workitem/11586) -- Applied Anthony's Sterling's Class Autoloader to reduce memory overhead by "Lazy Loading" of classes - @MarkBaker -- Modification to functions that accept a date parameter to support string values containing ordinals as per Excel (English language only) - @MarkBaker -- Modify PHPExcel_Style_NumberFormat::toFormattedString() to handle dates that fall outside of PHP's 32-bit date range - @MarkBaker -- Applied patch 5207 - @MarkBaker - -### Features - -- PHPExcel developer documentation: Set page margins - @Erik Tilt [CodePlex #11970](https://phpexcel.codeplex.com/workitem/11970) -- Special characters and accents in SYLK reader - @Erik Tilt [CodePlex #11038](https://phpexcel.codeplex.com/workitem/11038) -- Implement more Excel calculation functions - @MarkBaker - - Implemented the COUPDAYS(), COUPDAYBS(), COUPDAYSNC(), COUPNCD(), COUPPCD() and PRICE() Financial functions - - Implemented the N() and TYPE() Information functions - - Implemented the HYPERLINK() Lookup and Reference function- Horizontal page break support in PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11526](https://phpexcel.codeplex.com/workitem/11526) -- Introduce method setActiveSheetIndexByName() - @Erik Tilt [CodePlex #11529](https://phpexcel.codeplex.com/workitem/11529) -- AdvancedValueBinder.php: Automatically wrap text when there is new line in string (ALT+"Enter") - @Erik Tilt [CodePlex #11550](https://phpexcel.codeplex.com/workitem/11550) -- Data validation support in PHPExcel_Reader_Excel5 and PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10300](https://phpexcel.codeplex.com/workitem/10300) -- Improve autosize calculation - @MarkBaker [CodePlex #11616](https://phpexcel.codeplex.com/workitem/11616) -- Methods to translate locale-specific function names in formulae - @MarkBaker - - Language implementations for Czech (cs), Danish (da), German (de), English (uk), Spanish (es), Finnish (fi), French (fr), Hungarian (hu), Italian (it), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Brazilian Portuguese (pt_br), Russian (ru) and Swedish (sv)- Implement document properties in Excel5 reader/writer - @Erik Tilt [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759) - - Fixed so far for PHPExcel_Reader_Excel5- Show/hide row and column headers in worksheet - @Erik Tilt [CodePlex #11849](https://phpexcel.codeplex.com/workitem/11849) -- Can't set font on writing PDF (by key) - @Erik Tilt [CodePlex #11919](https://phpexcel.codeplex.com/workitem/11919) -- Thousands scale (1000^n) support in PHPExcel_Style_NumberFormat::toFormattedString - @Erik Tilt [CodePlex #12096](https://phpexcel.codeplex.com/workitem/12096) -- Implement repeating rows in PDF and HTML writer - @Erik Tilt -- Sheet tabs in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #12289](https://phpexcel.codeplex.com/workitem/12289) -- Add Wincache CachedObjectProvider - @MarkBaker [CodePlex #13041](https://phpexcel.codeplex.com/workitem/13041) -- Configure PDF Writer paper size based on Excel Page Settings value, and provided methods to override paper size and page orientation with the writer - @MarkBaker - - Note PHPExcel defaults to Letter size, while the previous PDF writer enforced A4 size, so PDF writer will now default to Letter- Initial implementation of cell caching: allowing larger workbooks to be managed, but at a cost in speed - @MarkBaker - -### Bugfixes - -- Added an identify() method to the IO Factory that identifies the reader which will be used to load a particular file without actually loading it. - @MarkBaker -- Warning messages with INDEX function having 2 arguments - @MarkBaker [CodePlex #10979](https://phpexcel.codeplex.com/workitem/10979) -- setValue('=') should result in string instead of formula - @Erik Tilt [CodePlex #11473](https://phpexcel.codeplex.com/workitem/11473) -- method _raiseFormulaError should no be private - @MarkBaker [CodePlex #11471](https://phpexcel.codeplex.com/workitem/11471) -- Fatal error: Call to undefined function mb_substr() in ...Classes\PHPExcel\Reader\Excel5.php on line 2903 - @Erik Tilt [CodePlex #11485](https://phpexcel.codeplex.com/workitem/11485) -- getBold(), getItallic(), getStrikeThrough() not always working with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #11487](https://phpexcel.codeplex.com/workitem/11487) -- AdvancedValueBinder.php not working correctly for $cell->setValue('hh:mm:ss') - @Erik Tilt [CodePlex #11492](https://phpexcel.codeplex.com/workitem/11492) -- Fixed leap year handling for the YEARFRAC() Date/Time function when basis ia 1 (Actual/actual) - @MarkBaker -- Warning messages - @MarkBaker [CodePlex #11490](https://phpexcel.codeplex.com/workitem/11490) - - Calculation Engine code modified to enforce strict standards for pass by reference- PHPExcel_Cell_AdvancedValueBinder doesnt work for dates in far future - @Erik Tilt [CodePlex #11483](https://phpexcel.codeplex.com/workitem/11483) -- MSODRAWING bug with long CONTINUE record in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #11528](https://phpexcel.codeplex.com/workitem/11528) -- PHPExcel_Reader_Excel2007 reads print titles as named range when there is more than one sheet - @Erik Tilt [CodePlex #11571](https://phpexcel.codeplex.com/workitem/11571) -- missing @return in phpdocblock in reader classes - @Erik Tilt [CodePlex #11561](https://phpexcel.codeplex.com/workitem/11561) -- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @Erik Tilt [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576) -- Small numbers escape treatment in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt [CodePlex #11588](https://phpexcel.codeplex.com/workitem/11588) -- Blank styled cells are not blank in output by HTML writer due to   - @Erik Tilt [CodePlex #11590](https://phpexcel.codeplex.com/workitem/11590) -- Calculation engine bug: Existing, blank cell + number gives #NUM - @MarkBaker [CodePlex #11587](https://phpexcel.codeplex.com/workitem/11587) -- AutoSize only measures length of first line in cell with multiple lines (ALT+Enter) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) -- Fatal error running Tests/12serializedfileformat.php (PHPExcel 1.7.2) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608) -- Fixed various errors in the WORKDAY() and NETWORKDAYS() Date/Time functions (particularly related to holidays) - @MarkBaker -- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/SheetView.php:115 - @Erik Tilt [CodePlex #11660](https://phpexcel.codeplex.com/workitem/11660) -- "Unrecognized token 39 in formula" with PHPExcel_Reader_Excel5 (occuring with add-in functions) - @Erik Tilt [CodePlex #11551](https://phpexcel.codeplex.com/workitem/11551) -- Excel2007 reader not reading PHPExcel_Style_Conditional::CONDITION_EXPRESSION - @Erik Tilt [CodePlex #11668](https://phpexcel.codeplex.com/workitem/11668) -- Fix to the BESSELI(), BESSELJ(), BESSELK(), BESSELY() and COMPLEX() Engineering functions to use correct default values for parameters - @MarkBaker -- DATEVALUE function not working for pure time values + allow DATEVALUE() function to handle partial dates (e.g. "1-Jun" or "12/2010") - @MarkBaker [CodePlex #11525](https://phpexcel.codeplex.com/workitem/11525) -- Fix for empty quoted strings in formulae - @MarkBaker -- Trap for division by zero in Bessel functions - @MarkBaker -- Fix to OOCalc Reader to convert semi-colon (;) argument separator in formulae to a comma (,) - @MarkBaker -- PHPExcel_Writer_Excel5_Parser cannot parse formula like =SUM(C$5:C5) - @Erik Tilt [CodePlex #11693](https://phpexcel.codeplex.com/workitem/11693) -- Fix to OOCalc Reader to handle dates that fall outside 32-bit PHP's date range - @MarkBaker -- File->sys_get_temp_dir() can fail in safe mode - @Erik Tilt [CodePlex #11692](https://phpexcel.codeplex.com/workitem/11692) -- Sheet references in Excel5 writer do not work when referenced sheet title contains non-Latin symbols - @Erik Tilt [CodePlex #11727](https://phpexcel.codeplex.com/workitem/11727) -- Bug in HTML writer can result in missing rows in output - @Erik Tilt [CodePlex #11743](https://phpexcel.codeplex.com/workitem/11743) -- setShowGridLines(true) not working with PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11674](https://phpexcel.codeplex.com/workitem/11674) -- PHPExcel_Worksheet_RowIterator initial position incorrect - @Erik Tilt [CodePlex #11836](https://phpexcel.codeplex.com/workitem/11836) -- PHPExcel_Worksheet_HeaderFooterDrawing Strict Exception thrown (by jshaw86) - @Erik Tilt [CodePlex #11835](https://phpexcel.codeplex.com/workitem/11835) -- Parts of worksheet lost when there are embedded charts (Excel5 reader) - @Erik Tilt [CodePlex #11850](https://phpexcel.codeplex.com/workitem/11850) -- VLOOKUP() function error when lookup value is passed as a cell reference rather than an absolute value - @MarkBaker -- First segment of Rich-Text not read correctly by PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #12041](https://phpexcel.codeplex.com/workitem/12041) -- Fatal Error with getCell('name') when name matches the pattern for a cell reference - @MarkBaker [CodePlex #12048](https://phpexcel.codeplex.com/workitem/12048) -- excel5 writer appears to be swapping image locations - @Erik Tilt [CodePlex #12039](https://phpexcel.codeplex.com/workitem/12039) -- Undefined index: host in ZipStreamWrapper.php, line 94 and line 101 - @Erik Tilt [CodePlex #11954](https://phpexcel.codeplex.com/workitem/11954) -- BIFF8 File Format problem (too short COLINFO record) - @Erik Tilt [CodePlex #11672](https://phpexcel.codeplex.com/workitem/11672) -- Column width sometimes changed after read/write with Excel2007 reader/writer - @Erik Tilt [CodePlex #12121](https://phpexcel.codeplex.com/workitem/12121) -- Worksheet.php throws a fatal error when styling is turned off via setReadDataOnly on the reader - @Erik Tilt [CodePlex #11964](https://phpexcel.codeplex.com/workitem/11964) -- Checking for Circular References in Formulae - @MarkBaker [CodePlex #11851](https://phpexcel.codeplex.com/workitem/11851) - - Calculation Engine code now traps for cyclic references, raising an error or throwing an exception, or allows 1 or more iterations through cyclic references, based on a configuration setting- PNG transparency using Excel2007 writer - @Erik Tilt [CodePlex #12244](https://phpexcel.codeplex.com/workitem/12244) -- Custom readfilter error when cell formulas reference excluded cells (Excel5 reader) - @Erik Tilt [CodePlex #12221](https://phpexcel.codeplex.com/workitem/12221) -- Protection problem in XLS - @Erik Tilt [CodePlex #12288](https://phpexcel.codeplex.com/workitem/12288) -- getColumnDimension()->setAutoSize() incorrect on cells with Number Formatting - @Erik Tilt [CodePlex #12300](https://phpexcel.codeplex.com/workitem/12300) -- Notices reading Excel file with Add-in funcitons (PHPExcel_Reader_Excel5) - @Erik Tilt [CodePlex #12378](https://phpexcel.codeplex.com/workitem/12378) -- Excel5 reader not reading formulas with deleted sheet references - @Erik Tilt [CodePlex #12380](https://phpexcel.codeplex.com/workitem/12380) -- Named range (defined name) scope problems for in PHPExcel - @Erik Tilt [CodePlex #12404](https://phpexcel.codeplex.com/workitem/12404) -- PHP Parse error: syntax error, unexpected T_PUBLIC in PHPExcel/Calculation.php on line 3482 - @Erik Tilt [CodePlex #12423](https://phpexcel.codeplex.com/workitem/12423) -- Named ranges don't appear in name box using Excel5 writer - @Erik Tilt [CodePlex #12505](https://phpexcel.codeplex.com/workitem/12505) -- Many merged cells + autoSize column -> slows down the writer - @Erik Tilt [CodePlex #12509](https://phpexcel.codeplex.com/workitem/12509) -- Incorrect fallback order comment in Shared/Strings.php ConvertEncoding() - @Erik Tilt [CodePlex #12539](https://phpexcel.codeplex.com/workitem/12539) -- IBM AIX iconv() will not work, should revert to mbstring etc. instead - @Erik Tilt [CodePlex #12538](https://phpexcel.codeplex.com/workitem/12538) -- Excel5 writer and mbstring functions overload - @Erik Tilt [CodePlex #12568](https://phpexcel.codeplex.com/workitem/12568) -- OFFSET needs to flattenSingleValue the $rows and $columns args - @MarkBaker [CodePlex #12672](https://phpexcel.codeplex.com/workitem/12672) -- Formula with DMAX(): Notice: Undefined offset: 2 in ...\PHPExcel\Calculation.php on line 2365 - @MarkBaker [CodePlex #12546](https://phpexcel.codeplex.com/workitem/12546) - - Note that the Database functions have not yet been implemented- Call to a member function getParent() on a non-object in Classes\\PHPExcel\\Calculation.php Title is required - @MarkBaker [CodePlex #12839](https://phpexcel.codeplex.com/workitem/12839) -- Cyclic Reference in Formula - @MarkBaker [CodePlex #12935](https://phpexcel.codeplex.com/workitem/12935) -- Memory error...data validation? - @MarkBaker [CodePlex #13025](https://phpexcel.codeplex.com/workitem/13025) - -## [1.7.2] - 2010-01-11 - -### General - -- Applied patch 4362 - @Erik Tilt -- Applied patch 4363 (modified) - @Erik Tilt -- 1.7.1 Extremely Slow - Refactored PHPExcel_Calculation_Functions::flattenArray() method and set calculation cache timer default to 2.5 seconds - @MarkBaker [CodePlex #10874](https://phpexcel.codeplex.com/workitem/10874) -- Allow formulae to contain line breaks - @MarkBaker -- split() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10910](https://phpexcel.codeplex.com/workitem/10910) -- sys_get_temp_dir() requires PHP 5.2.1, not PHP 5.2 [provide fallback function for PHP 5.2.0] - @Erik Tilt -- Implementation of the ISPMT() Financial function by Matt Groves - @MarkBaker -- Put the example of formula with more arguments in documentation - @MarkBaker [CodePlex #11052](https://phpexcel.codeplex.com/workitem/11052) - -### Features - -- Improved accuracy for the GAMMAINV() Statistical Function - @MarkBaker -- XFEXT record support to fix colors change from Excel5 reader, and copy/paste color change with Excel5 writer - @Erik Tilt [CodePlex #10409](https://phpexcel.codeplex.com/workitem/10409) - - Excel5 reader reads RGB color information in XFEXT records for borders, font color and fill color- Implement more Excel calculation functions - @MarkBaker - - Implemented the FVSCHEDULE(), XNPV(), IRR(), MIRR(), XIRR() and RATE() Financial functions - - Implemented the SUMPRODUCT() Mathematical function - - Implemented the ZTEST() Statistical Function- Multiple print areas in one sheet - @Erik Tilt [CodePlex #10919](https://phpexcel.codeplex.com/workitem/10919) -- Store calculated values in output by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10930](https://phpexcel.codeplex.com/workitem/10930) -- Sheet protection options in Excel5 reader/writer - @Erik Tilt [CodePlex #10939](https://phpexcel.codeplex.com/workitem/10939) -- Modification of the COUNT(), AVERAGE(), AVERAGEA(), DEVSQ, AVEDEV(), STDEV(), STDEVA(), STDEVP(), STDEVPA(), VARA() and VARPA() SKEW() and KURT() functions to correctly handle boolean values depending on whether they're passed in as values, values within a matrix or values within a range of cells. - @MarkBaker -- Cell range selection - @Erik Tilt -- Root-relative path handling - @MarkBaker [CodePlex #10266](https://phpexcel.codeplex.com/workitem/10266) - -### Bugfixes - -- Named Ranges not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #11315](https://phpexcel.codeplex.com/workitem/11315) -- Excel2007 Reader fails to load Apache POI generated Excel - @MarkBaker [CodePlex #11206](https://phpexcel.codeplex.com/workitem/11206) -- Number format is broken when system's thousands separator is empty - @MarkBaker [CodePlex #11154](https://phpexcel.codeplex.com/workitem/11154) -- ReferenceHelper::updateNamedFormulas throws errors if oldName is empty - @MarkBaker [CodePlex #11401](https://phpexcel.codeplex.com/workitem/11401) -- parse_url() fails to parse path to an image in xlsx - @MarkBaker [CodePlex #11296](https://phpexcel.codeplex.com/workitem/11296) -- Workaround for iconv_substr() bug in PHP 5.2.0 - @Erik Tilt [CodePlex #10876](https://phpexcel.codeplex.com/workitem/10876) -- 1 pixel error for image width and height with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10877](https://phpexcel.codeplex.com/workitem/10877) -- Fix to GEOMEAN() Statistical function - @MarkBaker -- setValue('-') and setValue('.') sets numeric 0 instead of 1-character string - @Erik Tilt [CodePlex #10884](https://phpexcel.codeplex.com/workitem/10884) -- Row height sometimes much too low after read with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10885](https://phpexcel.codeplex.com/workitem/10885) -- Diagonal border. Miscellaneous missing support. - @Erik Tilt [CodePlex #10888](https://phpexcel.codeplex.com/workitem/10888) - - Constant PHPExcel_Style_Borders::DIAGONAL_BOTH added to support double-diagonal (cross) - - PHPExcel_Reader_Excel2007 not always reading diagonal borders (only recognizes 'true' and not '1') - - PHPExcel_Reader_Excel5 support for diagonal borders - - PHPExcel_Writer_Excel5 support for diagonal borders- Session bug: Fatal error: Call to a member function bindValue() on a non-object in ...\Classes\PHPExcel\Cell.php on line 217 - @Erik Tilt [CodePlex #10894](https://phpexcel.codeplex.com/workitem/10894) -- Colors messed up saving twice with same instance of PHPExcel_Writer_Excel5 (regression since 1.7.0) - @Erik Tilt [CodePlex #10896](https://phpexcel.codeplex.com/workitem/10896) -- Method PHPExcel_Worksheet::setDefaultStyle is not working - @Erik Tilt [CodePlex #10917](https://phpexcel.codeplex.com/workitem/10917) -- PHPExcel_Reader_CSV::canRead() sometimes says false when it shouldn't - @Erik Tilt [CodePlex #10897](https://phpexcel.codeplex.com/workitem/10897) -- Changes in workbook not picked up between two saves with PHPExcel_Writer_Excel2007 - @Erik Tilt [CodePlex #10922](https://phpexcel.codeplex.com/workitem/10922) -- Decimal and thousands separators missing in HTML and PDF output - @Erik Tilt [CodePlex #10913](https://phpexcel.codeplex.com/workitem/10913) -- Notices with PHPExcel_Reader_Excel5 and named array constants - @Erik Tilt [CodePlex #10936](https://phpexcel.codeplex.com/workitem/10936) -- Calculation engine limitation on 32-bit platform with integers > 2147483647 - @MarkBaker [CodePlex #10938](https://phpexcel.codeplex.com/workitem/10938) -- Shared(?) formulae containing absolute cell references not read correctly using Excel5 Reader - @Erik Tilt [CodePlex #10959](https://phpexcel.codeplex.com/workitem/10959) -- Warning messages with intersection operator involving single cell - @MarkBaker [CodePlex #10962](https://phpexcel.codeplex.com/workitem/10962) -- Infinite loop in Excel5 reader caused by zero-length string in SST - @Erik Tilt [CodePlex #10980](https://phpexcel.codeplex.com/workitem/10980) -- Remove unnecessary cell sorting to improve speed by approx. 18% in HTML and PDF writers - @Erik Tilt [CodePlex #10983](https://phpexcel.codeplex.com/workitem/10983) -- Cannot read A1 cell content - OO_Reader - @MarkBaker [CodePlex #10977](https://phpexcel.codeplex.com/workitem/10977) -- Transliteration failed, invalid encoding - @Erik Tilt [CodePlex #11000](https://phpexcel.codeplex.com/workitem/11000) - -## [1.7.1] - 2009-11-02 - -### General - -- ereg() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10687](https://phpexcel.codeplex.com/workitem/10687) -- Writer Interface Inconsequence - setTempDir and setUseDiskCaching - @MarkBaker [CodePlex #10739](https://phpexcel.codeplex.com/workitem/10739) - -### Features - -- Upgrade to TCPDF 4.8.009 - @Erik Tilt -- Support for row and column styles (feature request) - @Erik Tilt - - Basic implementation for Excel2007/Excel5 reader/writer- Hyperlink to local file in Excel5 reader/writer - @Erik Tilt [CodePlex #10459](https://phpexcel.codeplex.com/workitem/10459) -- Color Tab (Color Sheet's name) - @MarkBaker [CodePlex #10472](https://phpexcel.codeplex.com/workitem/10472) -- Border style "double" support in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #10488](https://phpexcel.codeplex.com/workitem/10488) -- Multi-section number format support in HTML/PDF/CSV writers - @Erik Tilt [CodePlex #10492](https://phpexcel.codeplex.com/workitem/10492) -- Some additional performance tweaks in the calculation engine - @MarkBaker -- Fix result of DB() and DDB() Financial functions to 2dp when in Gnumeric Compatibility mode - @MarkBaker -- Added AMORDEGRC(), AMORLINC() and COUPNUM() Financial function (no validation of parameters yet) - @MarkBaker -- Improved accuracy of TBILLEQ(), TBILLPRICE() and TBILLYIELD() Financial functions when in Excel or Gnumeric mode - @MarkBaker -- Added INDIRECT() Lookup/Reference function (only supports full addresses at the moment) - @MarkBaker -- PHPExcel_Reader_CSV::canRead() improvements - @MarkBaker [CodePlex #10498](https://phpexcel.codeplex.com/workitem/10498) -- Input encoding option for PHPExcel_Reader_CSV - @Erik Tilt [CodePlex #10500](https://phpexcel.codeplex.com/workitem/10500) -- Colored number format support, e.g. [Red], in HTML/PDF output - @Erik Tilt [CodePlex #10493](https://phpexcel.codeplex.com/workitem/10493) -- Color Tab (Color Sheet's name) [Excel5 reader/writer support] - @Erik Tilt [CodePlex #10559](https://phpexcel.codeplex.com/workitem/10559) -- Initial version of SYLK (slk) and Excel 2003 XML Readers (Cell data and basic cell formatting) - @MarkBaker -- Initial version of Open Office Calc (ods) Reader (Cell data only) - @MarkBaker -- Initial use of "pass by reference" in the calculation engine for ROW() and COLUMN() Lookup/Reference functions - @MarkBaker -- COLUMNS() and ROWS() Lookup/Reference functions, and SUBSTITUTE() Text function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- AdvancedValueBinder(): Re-enable zero-padded string-to-number conversion, e.g '0004' -> 4 - @Erik Tilt [CodePlex #10502](https://phpexcel.codeplex.com/workitem/10502) -- Make PHP type match Excel datatype - @Erik Tilt [CodePlex #10600](https://phpexcel.codeplex.com/workitem/10600) -- Change first page number on header - @MarkBaker [CodePlex #10630](https://phpexcel.codeplex.com/workitem/10630) -- Applied patch 3941 - @MarkBaker -- Hidden sheets - @MB,ET [CodePlex #10745](https://phpexcel.codeplex.com/workitem/10745) -- mbstring fallback when iconv is broken - @Erik Tilt [CodePlex #10761](https://phpexcel.codeplex.com/workitem/10761) -- Note, can't yet handle comparison of two matrices - @MarkBaker -- Improved handling for validation and error trapping in a number of functions - @MarkBaker -- Improved support for fraction number formatting - @MarkBaker -- Support Reading CSV with Byte Order Mark (BOM) - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) - -### Bugfixes - -- addExternalSheet() at specified index - @Erik Tilt [CodePlex #10860](https://phpexcel.codeplex.com/workitem/10860) -- Named range can no longer be passed to worksheet->getCell() - @MarkBaker [CodePlex #10684](https://phpexcel.codeplex.com/workitem/10684) -- RichText HTML entities no longer working in PHPExcel 1.7.0 - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455) -- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet [+ support for simultaneous scale/fitToPage] - @Erik Tilt -- Performance issue identified by profiling - @MarkBaker [CodePlex #10469](https://phpexcel.codeplex.com/workitem/10469) -- setSelectedCell is wrong - @Erik Tilt [CodePlex #10473](https://phpexcel.codeplex.com/workitem/10473) -- Images get squeezed/stretched with (Mac) Verdana 10 Excel files using Excel5 reader/writer - @Erik Tilt [CodePlex #10481](https://phpexcel.codeplex.com/workitem/10481) -- Error in argument count for DATEDIF() function - @MarkBaker [CodePlex #10482](https://phpexcel.codeplex.com/workitem/10482) -- updateFormulaReferences is buggy - @MarkBaker [CodePlex #10452](https://phpexcel.codeplex.com/workitem/10452) -- CellIterator returns null Cell if onlyExistingCells is set and key() is in use - @MarkBaker [CodePlex #10485](https://phpexcel.codeplex.com/workitem/10485) -- Wrong RegEx for parsing cell references in formulas - @MarkBaker [CodePlex #10453](https://phpexcel.codeplex.com/workitem/10453) -- Optimisation subverted to devastating effect if IterateOnlyExistingCells is clear - @MarkBaker [CodePlex #10486](https://phpexcel.codeplex.com/workitem/10486) -- Fatal error: Uncaught exception 'Exception' with message 'Unrecognized token 6C in formula'... with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10494](https://phpexcel.codeplex.com/workitem/10494) -- Fractions stored as text are not treated as numbers by PHPExcel's calculation engine - @MarkBaker [CodePlex #10490](https://phpexcel.codeplex.com/workitem/10490) -- AutoFit (autosize) row height not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10503](https://phpexcel.codeplex.com/workitem/10503) -- Fixed problem with null values breaking the calculation stack - @MarkBaker -- Date number formats sometimes fail with PHPExcel_Style_NumberFormat::toFormattedString, e.g. [$-40047]mmmm d yyyy - @Erik Tilt [CodePlex #10524](https://phpexcel.codeplex.com/workitem/10524) -- Fixed minor problem with DATEDIFF YM calculation - @MarkBaker -- Applied patch 3695 - @MarkBaker -- setAutosize() and Date cells not working properly - @Erik Tilt [CodePlex #10536](https://phpexcel.codeplex.com/workitem/10536) -- Time value hour offset in output by HTML/PDF/CSV writers (system timezone problem) - @Erik Tilt [CodePlex #10556](https://phpexcel.codeplex.com/workitem/10556) -- Control characters 0x14-0x1F are not treated by PHPExcel - @Erik Tilt [CodePlex #10558](https://phpexcel.codeplex.com/workitem/10558) -- PHPExcel_Writer_Excel5 not working when open_basedir restriction is in effect - @Erik Tilt [CodePlex #10560](https://phpexcel.codeplex.com/workitem/10560) -- IF formula calculation problem in PHPExcel 1.7.0 (string comparisons) - @MarkBaker [CodePlex #10563](https://phpexcel.codeplex.com/workitem/10563) -- Improved CODE() Text function result for UTF-8 characters - @MarkBaker -- Empty rows are collapsed with HTML/PDF writer - @Erik Tilt [CodePlex #10568](https://phpexcel.codeplex.com/workitem/10568) -- Gaps between rows in output by PHPExcel_Writer_PDF (Upgrading to TCPDF 4.7.003) - @Erik Tilt [CodePlex #10569](https://phpexcel.codeplex.com/workitem/10569) -- Problem reading formulas (Excel5 reader problem with "fake" shared formulas) - @Erik Tilt [CodePlex #10575](https://phpexcel.codeplex.com/workitem/10575) -- Error type in formula: "_raiseFormulaError message is Formula Error: An unexpected error occured" - @MarkBaker [CodePlex #10588](https://phpexcel.codeplex.com/workitem/10588) -- Miscellaneous column width problems in Excel5/Excel2007 writer - @Erik Tilt [CodePlex #10599](https://phpexcel.codeplex.com/workitem/10599) -- Reader/Excel5 'Unrecognized token 2D in formula' in latest version - @Erik Tilt [CodePlex #10615](https://phpexcel.codeplex.com/workitem/10615) -- on php 5.3 PHPExcel 1.7 Excel 5 reader fails in _getNextToken, token = 2C, throws exception - @Erik Tilt [CodePlex #10623](https://phpexcel.codeplex.com/workitem/10623) -- Fatal error when altering styles after workbook has been saved - @Erik Tilt [CodePlex #10617](https://phpexcel.codeplex.com/workitem/10617) -- Images vertically stretched or squeezed when default font size is changed (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10661](https://phpexcel.codeplex.com/workitem/10661) -- Styles not read in "manipulated" Excel2007 workbook - @Erik Tilt [CodePlex #10676](https://phpexcel.codeplex.com/workitem/10676) -- Windows 7 says corrupt file by PHPExcel_Writer_Excel5 when opening in Excel - @Erik Tilt [CodePlex #10059](https://phpexcel.codeplex.com/workitem/10059) -- Calculations sometimes not working with cell references to other sheets - @MarkBaker [CodePlex #10708](https://phpexcel.codeplex.com/workitem/10708) -- Problem with merged cells after insertNewRowBefore() - @Erik Tilt [CodePlex #10706](https://phpexcel.codeplex.com/workitem/10706) -- Applied patch 4023 - @MarkBaker -- Fix to SUMIF() and COUNTIF() Statistical functions for when condition is a match against a string value - @MarkBaker -- PHPExcel_Cell::coordinateFromString should throw exception for bad string parameter - @Erik Tilt [CodePlex #10721](https://phpexcel.codeplex.com/workitem/10721) -- EucrosiaUPC (Thai font) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10723](https://phpexcel.codeplex.com/workitem/10723) -- Improved the return of calculated results when the result value is an array - @MarkBaker -- Allow calculation engine to support Functions prefixed with @ within formulae - @MarkBaker -- Intersection operator (space operator) fatal error with calculation engine - @MarkBaker [CodePlex #10632](https://phpexcel.codeplex.com/workitem/10632) -- Chinese, Japanese, Korean characters show as squares in PDF - @Erik Tilt [CodePlex #10742](https://phpexcel.codeplex.com/workitem/10742) -- sheet title allows invalid characters - @Erik Tilt [CodePlex #10756](https://phpexcel.codeplex.com/workitem/10756) -- Sheet!$A$1 as function argument in formula causes infinite loop in Excel5 writer - @Erik Tilt [CodePlex #10757](https://phpexcel.codeplex.com/workitem/10757) -- Cell range involving name not working with calculation engine - Modified calculation parser to handle range operator (:), but doesn't currently handle worksheet references with spaces or other non-alphameric characters, or trap erroneous references - @MarkBaker [CodePlex #10740](https://phpexcel.codeplex.com/workitem/10740) -- DATE function problem with calculation engine (says too few arguments given) - @MarkBaker [CodePlex #10798](https://phpexcel.codeplex.com/workitem/10798) -- Blank cell can cause wrong calculated value - @MarkBaker [CodePlex #10799](https://phpexcel.codeplex.com/workitem/10799) -- Modified ROW() and COLUMN() Lookup/Reference Functions to return an array when passed a cell range, plus some additional work on INDEX() - @MarkBaker -- Images not showing in Excel 97 using PHPExcel_Writer_Excel5 (patch by Jordi Gutiérrez Hermoso) - @Erik Tilt [CodePlex #10817](https://phpexcel.codeplex.com/workitem/10817) -- When figures are contained in the excel sheet, Reader was stopped - @Erik Tilt [CodePlex #10785](https://phpexcel.codeplex.com/workitem/10785) -- Formulas changed after insertNewRowBefore() - @MarkBaker [CodePlex #10818](https://phpexcel.codeplex.com/workitem/10818) -- Cell range row offset problem with shared formulas using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10825](https://phpexcel.codeplex.com/workitem/10825) -- Warning: Call-time pass-by-reference has been deprecated - @MarkBaker [CodePlex #10832](https://phpexcel.codeplex.com/workitem/10832) -- Image should "Move but don't size with cells" instead of "Move and size with cells" with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10849](https://phpexcel.codeplex.com/workitem/10849) -- Opening a Excel5 generated XLS in Excel 2007 results in header/footer entry not showing on input - @Erik Tilt [CodePlex #10856](https://phpexcel.codeplex.com/workitem/10856) -- addExternalSheet() not returning worksheet - @Erik Tilt [CodePlex #10859](https://phpexcel.codeplex.com/workitem/10859) -- Invalid results in formulas with named ranges - @MarkBaker [CodePlex #10629](https://phpexcel.codeplex.com/workitem/10629) - -## [1.7.0] - 2009-08-10 - -### General - -- Expand documentation: Number formats - @Erik Tilt -- Class 'PHPExcel_Cell_AdvancedValueBinder' not found - @Erik Tilt - -### Features - -- Change return type of date functions to PHPExcel_Calculation_Functions::RETURNDATE_EXCEL - @MarkBaker -- New RPN and stack-based calculation engine for improved performance of formula calculation - @MarkBaker - - Faster (anything between 2 and 12 times faster than the old parser, depending on the complexity and nature of the formula) - - Significantly more memory efficient when formulae reference cells across worksheets - - Correct behaviour when referencing Named Ranges that exist on several worksheets - - Support for Excel ^ (Exponential) and % (Percentage) operators - - Support for matrices within basic arithmetic formulae (e.g. ={1,2,3;4,5,6;7,8,9}/2) - - Better trapping/handling of NaN and infinity results (return #NUM! error) - - Improved handling of empty parameters for Excel functions - - Optional logging of calculation steps- New calculation engine can be accessed independently of workbooks (for use as a standalone calculator) - @MarkBaker -- Implement more Excel calculation functions - @MarkBaker - - Initial implementation of the COUNTIF() and SUMIF() Statistical functions - - Added ACCRINT() Financial function- Modifications to number format handling for dddd and ddd masks in dates, use of thousand separators even when locale only implements it for money, and basic fraction masks (0 ?/? and ?/?) - @MarkBaker -- Support arbitrary fixed number of decimals in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt -- Improving performance and memory on data dumps - @Erik Tilt - - Various style optimizations (merging from branch wi6857-memory) - - Moving hyperlink and dataValidation properties from cell to worksheet for lower PHP memory usage- Provide fluent interfaces where possible - @MarkBaker -- Make easy way to apply a border to a rectangular selection - @Erik Tilt -- Support for system window colors in PHPExcel_Reader_Excel5 - @Erik Tilt -- Horizontal center across selection - @Erik Tilt -- Merged cells record, write to full record size in PHPExcel_Writer_Excel5 - @Erik Tilt -- Add page break between sheets in exported PDF - @MarkBaker -- Sanitization of UTF-8 input for cell values - @Erik Tilt -- Read cached calculated value with PHPExcel_Reader_Excel5 - @Erik Tilt -- Miscellaneous CSS improvements for PHPExcel_Writer_HTML - @Erik Tilt -- getProperties: setCompany feature request - @Erik Tilt -- Insert worksheet at a specified index - @MarkBaker -- Change worksheet index - @MarkBaker -- Readfilter for CSV reader - @MarkBaker -- Check value of mbstring.func_overload when saving with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10172](https://phpexcel.codeplex.com/workitem/10172) -- Eliminate dependency of an include path pointing to class directory - @Erik Tilt [CodePlex #10251](https://phpexcel.codeplex.com/workitem/10251) -- Method for getting the correct reader for a certain file (contribution) - @Erik Tilt [CodePlex #10292](https://phpexcel.codeplex.com/workitem/10292) -- Choosing specific row in fromArray method - @Erik Tilt [CodePlex #10287](https://phpexcel.codeplex.com/workitem/10287) -- Shared formula support in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10319](https://phpexcel.codeplex.com/workitem/10319) - -### Bugfixes - -- Right-to-left column direction in worksheet - @MB,ET [CodePlex #10345](https://phpexcel.codeplex.com/workitem/10345) -- PHPExcel_Reader_Excel5 not reading PHPExcel_Style_NumberFormat::FORMAT_NUMBER ('0') - @Erik Tilt -- Fractional row height in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt -- Fractional (decimal) numbers not inserted correctly when locale is other than English - @Erik Tilt -- Fractional calculated value in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt -- Locale aware decimal and thousands separator in exported formats HTML, CSV, PDF - @Erik Tilt -- Cannot Add Image with Space on its Name - @MarkBaker -- Black line at top of every page in output by PHPExcel_Writer_PDF - @Erik Tilt -- Border styles and border colors not showing in HTML output (regression since 1.6.4) - @Erik Tilt -- Hidden screen gridlines setting in worksheet not read by PHPExcel_Reader_Excel2007 - @Erik Tilt -- Some valid sheet names causes corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker -- More than 32,767 characters in a cell gives corrupt Excel file - @Erik Tilt -- Images not getting copyied with the ->copy() function - @Erik Tilt -- Bad calculation of column width setAutoSize(true) function - @Erik Tilt -- Dates are sometimes offset by 1 day in output by HTML and PDF writers depending on system timezone setting - @Erik Tilt -- Wingdings symbol fonts not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10003](https://phpexcel.codeplex.com/workitem/10003) -- White space string prefix stripped by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #10010](https://phpexcel.codeplex.com/workitem/10010) -- The name of the Workbook stream MUST be "Workbook", not "Book" - @Erik Tilt [CodePlex #10023](https://phpexcel.codeplex.com/workitem/10023) -- Avoid message "Microsoft Excel recalculates formulas..." when closing xls file from Excel - @Erik Tilt [CodePlex #10030](https://phpexcel.codeplex.com/workitem/10030) -- Non-unique newline representation causes problems with LEN formula - @Erik Tilt [CodePlex #10031](https://phpexcel.codeplex.com/workitem/10031) -- Newline in cell not showing with PHPExcel_Writer_HTML and PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10033](https://phpexcel.codeplex.com/workitem/10033) -- Rich-Text strings get prefixed by   when output by HTML writer - @Erik Tilt [CodePlex #10046](https://phpexcel.codeplex.com/workitem/10046) -- Leading spaces do not appear in output by HTML/PDF writers - @Erik Tilt [CodePlex #10052](https://phpexcel.codeplex.com/workitem/10052) -- Empty Apache POI-generated file can not be read - @MarkBaker [CodePlex #10061](https://phpexcel.codeplex.com/workitem/10061) -- Column width not scaling correctly with font size in HTML and PDF writers - @Erik Tilt [CodePlex #10068](https://phpexcel.codeplex.com/workitem/10068) -- Inaccurate row heights with HTML writer - @Erik Tilt [CodePlex #10069](https://phpexcel.codeplex.com/workitem/10069) -- Reference helper - @MarkBaker -- Excel 5 Named ranges should not be local to the worksheet, but accessible from all worksheets - @MarkBaker -- Row heights are ignored by PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10088](https://phpexcel.codeplex.com/workitem/10088) -- Write raw XML - @MarkBaker -- removeRow(), removeColumn() not always clearing cell values - @Erik Tilt [CodePlex #10098](https://phpexcel.codeplex.com/workitem/10098) -- Problem reading certain hyperlink records with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10142](https://phpexcel.codeplex.com/workitem/10142) -- Hyperlink cell range read failure with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #10143](https://phpexcel.codeplex.com/workitem/10143) -- 'Column string index can not be empty.' - @MarkBaker [CodePlex #10149](https://phpexcel.codeplex.com/workitem/10149) -- getHighestColumn() sometimes says there are 256 columns with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10204](https://phpexcel.codeplex.com/workitem/10204) -- extractSheetTitle fails when sheet title contains exclamation mark (!) - @Erik Tilt [CodePlex #10220](https://phpexcel.codeplex.com/workitem/10220) -- setTitle() sometimes erroneously appends integer to sheet name - @Erik Tilt [CodePlex #10221](https://phpexcel.codeplex.com/workitem/10221) -- Mac BIFF5 Excel file read failure (missing support for Mac OS Roman character set) - @Erik Tilt [CodePlex #10229](https://phpexcel.codeplex.com/workitem/10229) -- BIFF5 header and footer incorrectly read by PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10230](https://phpexcel.codeplex.com/workitem/10230) -- iconv notices when reading hyperlinks with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10259](https://phpexcel.codeplex.com/workitem/10259) -- Excel5 reader OLE read failure with small Mac BIFF5 Excel files - @Erik Tilt [CodePlex #10252](https://phpexcel.codeplex.com/workitem/10252) -- Problem in reading formula : IF( IF ) with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10272](https://phpexcel.codeplex.com/workitem/10272) -- Error reading formulas referencing external sheets with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10274](https://phpexcel.codeplex.com/workitem/10274) -- Image horizontally stretched when default font size is increased (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10291](https://phpexcel.codeplex.com/workitem/10291) -- Undefined offset in Reader\Excel5.php on line 3572 - @Erik Tilt [CodePlex #10333](https://phpexcel.codeplex.com/workitem/10333) -- PDF output different then XLS (copied data) - @MarkBaker [CodePlex #10340](https://phpexcel.codeplex.com/workitem/10340) -- Internal hyperlinks with UTF-8 sheet names not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10352](https://phpexcel.codeplex.com/workitem/10352) -- String shared formula result read error with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10361](https://phpexcel.codeplex.com/workitem/10361) -- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/PageSetup.php:338 - @Erik Tilt [CodePlex #10363](https://phpexcel.codeplex.com/workitem/10363) -- Using setLoadSheetsOnly fails if you do not use setReadDataOnly(true) and sheet is not the first sheet - @Erik Tilt [CodePlex #10355](https://phpexcel.codeplex.com/workitem/10355) -- getCalculatedValue() sometimes incorrect with IF formula and 0-values - @MarkBaker [CodePlex #10362](https://phpexcel.codeplex.com/workitem/10362) -- Excel Reader 2007 problem with "shared" formulae when "master" is an error - @MarkBaker -- Named Range Bug, using the same range name on different worksheets - @MarkBaker -- Java code in JAMA classes - @MarkBaker -- getCalculatedValue() not working with some formulas involving error types - @MarkBaker -- evaluation of both return values in an IF() statement returning an error if either result was an error, irrespective of the IF evaluation - @MarkBaker -- Power in formulas: new calculation engine no longer treats ^ as a bitwise XOR operator - @MarkBaker -- Bugfixes and improvements to many of the Excel functions in PHPExcel - @MarkBaker - - Added optional "places" parameter in the BIN2HEX(), BIN2OCT, DEC2BIN(), DEC2OCT(), DEC2HEX(), HEX2BIN(), HEX2OCT(), OCT2BIN() and OCT2HEX() Engineering Functions - - Trap for unbalanced matrix sizes in MDETERM() and MINVERSE() Mathematic and Trigonometric functions - - Fix for default characters parameter value for LEFT() and RIGHT() Text functions - - Fix for GCD() and LCB() Mathematical functions when the parameters include a zero (0) value - - Fix for BIN2OCT() Engineering Function for 2s complement values (which were returning hex values) - - Fix for BESSELK() and BESSELY() Engineering functions - - Fix for IMDIV() Engineering Function when result imaginary component is positive (wasn't setting the sign) - - Fix for ERF() Engineering Function when called with an upper limit value for the integration - - Fix to DATE() Date/Time Function for year value of 0 - - Set ISPMT() function as category FINANCIAL - - Fix for DOLLARDE() and DOLLARFR() Financial functions - - Fix to EFFECT() Financial function (treating $nominal_rate value as a variable name rather than a value) - - Fix to CRITBINOM() Statistical function (CurrentValue and EssentiallyZero treated as constants rather than variables) - - Note that an Error in the function logic can still lead to a permanent loop - - Fix to MOD() Mathematical function to work with floating point results - - Fix for QUOTIENT() Mathematical function - - Fix to HOUR(), MINUTE() and SECOND() Date/Time functions to return an error when passing in a floating point value of 1.0 or greater, or less than 0 - - LOG() Function now correctly returns base-10 log when called with only one parameter, rather than the natural log as the default base - - Modified text functions to handle multibyte character set (UTF-8). - -## [1.6.7] - 2009-04-22 - -### BREAKING CHANGE - -In previous versions of PHPExcel up to and including 1.6.6, -when a cell had a date-like number format code, it was possible to enter a date -directly using an integer PHP-time without converting to Excel date format. -Starting with PHPExcel 1.6.7 this is no longer supported. Refer to the developer -documentation for more information on entering dates into a cell. - -### General - -- Deprecate misspelled setStriketrough() and getStriketrough() methods - @MarkBaker [CodePlex #9416](https://phpexcel.codeplex.com/workitem/9416) - -### Features - -- Performance improvement when saving file - @MarkBaker [CodePlex #9526](https://phpexcel.codeplex.com/workitem/9526) -- Check that sheet title has maximum 31 characters - @MarkBaker [CodePlex #9598](https://phpexcel.codeplex.com/workitem/9598) -- True support for Excel built-in number format codes - @MB, ET [CodePlex #9631](https://phpexcel.codeplex.com/workitem/9631) -- Ability to read defect BIFF5 Excel file without CODEPAGE record - @Erik Tilt [CodePlex #9683](https://phpexcel.codeplex.com/workitem/9683) -- Auto-detect which reader to invoke - @MarkBaker [CodePlex #9701](https://phpexcel.codeplex.com/workitem/9701) -- Deprecate insertion of dates using PHP-time (Unix time) [request for removal of feature] - @Erik Tilt [CodePlex #9214](https://phpexcel.codeplex.com/workitem/9214) -- Support for entering time values like '9:45', '09:45' using AdvancedValueBinder - @Erik Tilt [CodePlex #9747](https://phpexcel.codeplex.com/workitem/9747) - -### Bugfixes - -- DataType dependent horizontal alignment in HTML and PDF writer - @Erik Tilt [CodePlex #9797](https://phpexcel.codeplex.com/workitem/9797) -- Cloning data validation object causes script to stop - @MarkBaker [CodePlex #9375](https://phpexcel.codeplex.com/workitem/9375) -- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9400](https://phpexcel.codeplex.com/workitem/9400) -- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9399](https://phpexcel.codeplex.com/workitem/9399) -- Row outline level not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9437](https://phpexcel.codeplex.com/workitem/9437) -- Occasional notices with PHPExcel_Reader_Excel5 when Excel file contains drawing elements - @Erik Tilt [CodePlex #9452](https://phpexcel.codeplex.com/workitem/9452) -- PHPExcel_Reader_Excel5 fails as a whole when workbook contains images other than JPEG/PNG - @Erik Tilt [CodePlex #9453](https://phpexcel.codeplex.com/workitem/9453) -- Excel5 writer checks for iconv but does not necessarily use it - @Erik Tilt [CodePlex #9444](https://phpexcel.codeplex.com/workitem/9444) -- Altering a style on copied worksheet alters also the original - @Erik Tilt [CodePlex #9463](https://phpexcel.codeplex.com/workitem/9463) -- Formulas are incorrectly updated when a sheet is renamed - @MarkBaker [CodePlex #9480](https://phpexcel.codeplex.com/workitem/9480) -- PHPExcel_Worksheet::extractSheetTitle not treating single quotes correctly - @MarkBaker [CodePlex #9513](https://phpexcel.codeplex.com/workitem/9513) -- PHP Warning raised in function array_key_exists - @MarkBaker [CodePlex #9477](https://phpexcel.codeplex.com/workitem/9477) -- getAlignWithMargins() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9599](https://phpexcel.codeplex.com/workitem/9599) -- getScaleWithDocument() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9600](https://phpexcel.codeplex.com/workitem/9600) -- PHPExcel_Reader_Excel2007 not reading the first user-defined number format - @MarkBaker [CodePlex #9630](https://phpexcel.codeplex.com/workitem/9630) -- Print area converted to uppercase after read with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9647](https://phpexcel.codeplex.com/workitem/9647) -- Incorrect reading of scope for named range using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9661](https://phpexcel.codeplex.com/workitem/9661) -- Error with pattern (getFillType) and rbg (getRGB) - @MarkBaker [CodePlex #9690](https://phpexcel.codeplex.com/workitem/9690) -- AdvancedValueBinder affected by system timezone setting when inserting date values - @Erik Tilt [CodePlex #9712](https://phpexcel.codeplex.com/workitem/9712) -- PHPExcel_Reader_Excel2007 not reading value of active sheet index - @Erik Tilt [CodePlex #9743](https://phpexcel.codeplex.com/workitem/9743) -- getARGB() sometimes returns SimpleXMLElement object instead of string with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #9742](https://phpexcel.codeplex.com/workitem/9742) -- Negative image offset causes defects in 14excel5.xls and 20readexcel5.xlsx - @Erik Tilt [CodePlex #9731](https://phpexcel.codeplex.com/workitem/9731) -- HTML & PDF Writer not working with mergeCells (regression since 1.6.5) - @Erik Tilt [CodePlex #9758](https://phpexcel.codeplex.com/workitem/9758) -- Too wide columns with HTML and PDF writer - @Erik Tilt [CodePlex #9774](https://phpexcel.codeplex.com/workitem/9774) -- PDF and cyrillic fonts - @MarkBaker [CodePlex #9775](https://phpexcel.codeplex.com/workitem/9775) -- Percentages not working correctly with HTML and PDF writers (shows 0.25% instead of 25%) - @Erik Tilt [CodePlex #9793](https://phpexcel.codeplex.com/workitem/9793) -- PHPExcel_Writer_HTML creates extra borders around cell contents using setUseInlineCss(true) - @Erik Tilt [CodePlex #9791](https://phpexcel.codeplex.com/workitem/9791) -- Problem with text wrap + merged cells in HTML and PDF writer - @Erik Tilt [CodePlex #9784](https://phpexcel.codeplex.com/workitem/9784) -- Adjacent path separators in include_path causing IOFactory to violate open_basedir restriction - @Erik Tilt [CodePlex #9814](https://phpexcel.codeplex.com/workitem/9814) - -## [1.6.6] - 2009-03-02 - -### General - -- Improve support for built-in number formats in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9102](https://phpexcel.codeplex.com/workitem/9102) -- Source files are in both UNIX and DOS formats - changed to UNIX - @Erik Tilt [CodePlex #9281](https://phpexcel.codeplex.com/workitem/9281) - -### Features - -- Update documentation: Which language to write formulas in? - @MarkBaker [CodePlex #9338](https://phpexcel.codeplex.com/workitem/9338) -- Ignore DEFCOLWIDTH records with value 8 in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8817](https://phpexcel.codeplex.com/workitem/8817) -- Support for width, height, offsetX, offsetY for images in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8847](https://phpexcel.codeplex.com/workitem/8847) -- Disk Caching in specific folder - @MarkBaker [CodePlex #8870](https://phpexcel.codeplex.com/workitem/8870) -- Added SUMX2MY2, SUMX2PY2, SUMXMY2, MDETERM and MINVERSE Mathematical and Trigonometric Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added CONVERT Engineering Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added DB, DDB, DISC, DOLLARDE, DOLLARFR, INTRATE, IPMT, PPMT, PRICEDISC, PRICEMAT and RECEIVED Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added ACCRINTM, CUMIPMT, CUMPRINC, TBILLEQ, TBILLPRICE, TBILLYIELD, YIELDDISC and YIELDMAT Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added DOLLAR Text Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added CORREL, COVAR, FORECAST, INTERCEPT, RSQ, SLOPE and STEYX Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added PEARSON Statistical Functions as a synonym for CORREL - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added LINEST, LOGEST (currently only valid for stats = false), TREND and GROWTH Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added RANK and PERCENTRANK Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Added ROMAN Mathematical Function (Classic form only) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Update documentation to show example of getCellByColumnAndRow($col, $row) - @MarkBaker [CodePlex #8931](https://phpexcel.codeplex.com/workitem/8931) -- Implement worksheet, row and cell iterators - @MarkBaker [CodePlex #8770](https://phpexcel.codeplex.com/workitem/8770) -- Support for arbitrary defined names (named range) - @MarkBaker [CodePlex #9001](https://phpexcel.codeplex.com/workitem/9001) -- Update formulas when sheet title / named range title changes - @MB, ET [CodePlex #9016](https://phpexcel.codeplex.com/workitem/9016) -- Ability to read cached calculated value - @MarkBaker [CodePlex #9103](https://phpexcel.codeplex.com/workitem/9103) -- Support for Excel 1904 calendar date mode (Mac) - @MBaker, ET [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) -- PHPExcel_Writer_Excel5 improvements writing shared strings table - @Erik Tilt [CodePlex #9194](https://phpexcel.codeplex.com/workitem/9194) -- PHPExcel_Writer_Excel5 iconv fallback when mbstring extension is not enabled - @Erik Tilt [CodePlex #9248](https://phpexcel.codeplex.com/workitem/9248) -- UTF-8 support in font names in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9253](https://phpexcel.codeplex.com/workitem/9253) -- Implement value binding architecture - @MarkBaker [CodePlex #9215](https://phpexcel.codeplex.com/workitem/9215) -- PDF writer not working with UTF-8 - @MarkBaker [CodePlex #6742](https://phpexcel.codeplex.com/workitem/6742) - -### Bugfixes - -- Eliminate duplicate style entries in multisheet workbook written by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9355](https://phpexcel.codeplex.com/workitem/9355) -- Redirect to client browser fails due to trailing white space in class definitions - @Erik Tilt [CodePlex #8810](https://phpexcel.codeplex.com/workitem/8810) -- Spurious column dimension element introduced in blank worksheet after using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8816](https://phpexcel.codeplex.com/workitem/8816) -- Image gets slightly narrower than expected when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8830](https://phpexcel.codeplex.com/workitem/8830) -- Image laid over non-visible row gets squeezed in height when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8831](https://phpexcel.codeplex.com/workitem/8831) -- PHPExcel_Reader_Excel5 fails when there are 10 or more images in the workbook - @Erik Tilt [CodePlex #8860](https://phpexcel.codeplex.com/workitem/8860) -- Different header/footer images in different sheets not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8909](https://phpexcel.codeplex.com/workitem/8909) -- Fractional seconds disappear when using PHPExcel_Reader_Excel2007 and PHPExcel_Reader_Excel5 - @MB, ET [CodePlex #8924](https://phpexcel.codeplex.com/workitem/8924) -- Images not showing in OpenOffice when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7994](https://phpexcel.codeplex.com/workitem/7994) -- Images not showing on print using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9047](https://phpexcel.codeplex.com/workitem/9047) -- PHPExcel_Writer_Excel5 maximum allowed record size 4 bytes too short - @Erik Tilt [CodePlex #9085](https://phpexcel.codeplex.com/workitem/9085) -- Not numeric strings are formatted as dates and numbers using worksheet's toArray method - @MarkBaker [CodePlex #9119](https://phpexcel.codeplex.com/workitem/9119) -- Excel5 simple formula parsing error - @Erik Tilt [CodePlex #9132](https://phpexcel.codeplex.com/workitem/9132) -- Problems writing dates with CSV - @Erik Tilt [CodePlex #9206](https://phpexcel.codeplex.com/workitem/9206) -- PHPExcel_Reader_Excel5 reader fails with fatal error when reading group shapes - @Erik Tilt [CodePlex #9203](https://phpexcel.codeplex.com/workitem/9203) -- PHPExcel_Writer_Excel5 fails completely when workbook contains more than 57 colors - @Erik Tilt [CodePlex #9231](https://phpexcel.codeplex.com/workitem/9231) -- PHPExcel_Writer_PDF not compatible with autoload - @Erik Tilt [CodePlex #9244](https://phpexcel.codeplex.com/workitem/9244) -- Fatal error: Call to a member function getNestingLevel() on a non-object in PHPExcel/Reader/Excel5.php on line 690 - @Erik Tilt [CodePlex #9250](https://phpexcel.codeplex.com/workitem/9250) -- Notices when running test 04printing.php on PHP 5.2.8 - @MarkBaker [CodePlex #9246](https://phpexcel.codeplex.com/workitem/9246) -- insertColumn() spawns creation of spurious RowDimension - @MarkBaker [CodePlex #9294](https://phpexcel.codeplex.com/workitem/9294) -- Fix declarations for methods in extended Trend classes - @MarkBaker [CodePlex #9296](https://phpexcel.codeplex.com/workitem/9296) -- Fix to parameters for the FORECAST Statistical Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- PDF writer problems with cell height and text wrapping - @MarkBaker [CodePlex #7083](https://phpexcel.codeplex.com/workitem/7083) -- Fix test for calculated value in case the returned result is an array - @MarkBaker -- Column greater than 256 results in corrupt Excel file using PHPExcel_Writer_Excel5 - @Erik Tilt -- Excel Numberformat 0.00 results in non internal decimal places values in toArray() Method - @MarkBaker [CodePlex #9351](https://phpexcel.codeplex.com/workitem/9351) -- setAutoSize not taking into account text rotation - @MB,ET [CodePlex #9356](https://phpexcel.codeplex.com/workitem/9356) -- Call to undefined method PHPExcel_Worksheet_MemoryDrawing::getPath() in PHPExcel/Writer/HTML.php - @Erik Tilt [CodePlex #9372](https://phpexcel.codeplex.com/workitem/9372) - -## [1.6.5] - 2009-01-05 - -### General - -- Applied patch 2063 - @MarkBaker -- Optimise Shared Strings - @MarkBaker -- Optimise Cell Sorting - @MarkBaker -- Optimise Style Hashing - @MarkBaker -- UTF-8 enhancements - @Erik Tilt -- PHPExcel_Writer_HTML validation errors against strict HTML 4.01 / CSS 2.1 - @Erik Tilt -- Documented work items 6203 and 8110 in manual - @MarkBaker -- Restructure package hierachy so classes can be found more easily in auto-generated API (from work item 8468) - @Erik Tilt - -### Features - -- Redirect output to a client's browser: Update recommendation in documentation - @MarkBaker [CodePlex #8806](https://phpexcel.codeplex.com/workitem/8806) -- PHPExcel_Reader_Excel5 support for print gridlines - @Erik Tilt [CodePlex #7897](https://phpexcel.codeplex.com/workitem/7897) -- Screen gridlines support in Excel5 reader/writer - @Erik Tilt [CodePlex #7899](https://phpexcel.codeplex.com/workitem/7899) -- Option for adding image to spreadsheet from image resource in memory - @MB, ET [CodePlex #7552](https://phpexcel.codeplex.com/workitem/7552) -- PHPExcel_Reader_Excel5 style support for BIFF5 files (Excel 5.0 - Excel 95) - @Erik Tilt [CodePlex #7862](https://phpexcel.codeplex.com/workitem/7862) -- PHPExcel_Reader_Excel5 support for user-defined colors and special built-in colors - @Erik Tilt [CodePlex #7918](https://phpexcel.codeplex.com/workitem/7918) -- Support for freeze panes in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7992](https://phpexcel.codeplex.com/workitem/7992) -- Support for header and footer margins in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7996](https://phpexcel.codeplex.com/workitem/7996) -- Support for active sheet index in Excel5 reader/writer - @Erik Tilt [CodePlex #7997](https://phpexcel.codeplex.com/workitem/7997) -- Freeze panes not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7991](https://phpexcel.codeplex.com/workitem/7991) -- Support for screen zoom level (feature request) - @MB, ET [CodePlex #7993](https://phpexcel.codeplex.com/workitem/7993) -- Support for default style in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8012](https://phpexcel.codeplex.com/workitem/8012) -- Apple iWork / Numbers.app incompatibility - @MarkBaker [CodePlex #8094](https://phpexcel.codeplex.com/workitem/8094) -- Support "between rule" in conditional formatting - @MarkBaker [CodePlex #7931](https://phpexcel.codeplex.com/workitem/7931) -- Comment size, width and height control (feature request) - @MarkBaker [CodePlex #8308](https://phpexcel.codeplex.com/workitem/8308) -- Improve method for storing MERGEDCELLS records in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8418](https://phpexcel.codeplex.com/workitem/8418) -- Support for protectCells() in Excel5 reader/writer - @Erik Tilt [CodePlex #8435](https://phpexcel.codeplex.com/workitem/8435) -- Support for fitToWidth and fitToHeight pagesetup properties in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8472](https://phpexcel.codeplex.com/workitem/8472) -- Support for setShowSummaryBelow() and setShowSummaryRight() in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8489](https://phpexcel.codeplex.com/workitem/8489) -- Support for Excel 1904 calendar date mode (Mac) - @MarkBaker [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483) -- Excel5 reader: Support for reading images (bitmaps) - @Erik Tilt [CodePlex #7538](https://phpexcel.codeplex.com/workitem/7538) -- Support for default style in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8787](https://phpexcel.codeplex.com/workitem/8787) -- Modified calculate() method to return either an array or the first value from the array for those functions that return arrays rather than single values (e.g the MMULT and TRANSPOSE function). This performance can be modified based on the $returnArrayAsType which can be set/retrieved by calling the setArrayReturnType() and getArrayReturnType() methods of the PHPExcel_Calculation class. - @MarkBaker - -### Bugfixes - -- Added ERROR.TYPE Information Function, MMULT Mathematical and Trigonometry Function, and TRANSPOSE Lookup and Reference Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- setPrintGridlines(true) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7896](https://phpexcel.codeplex.com/workitem/7896) -- Incorrect mapping of fill patterns in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7907](https://phpexcel.codeplex.com/workitem/7907) -- setShowGridlines(false) not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7898](https://phpexcel.codeplex.com/workitem/7898) -- getShowGridlines() gives inverted value when reading sheet with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7905](https://phpexcel.codeplex.com/workitem/7905) -- User-defined column width becomes slightly larger after read/write with Excel5 - @Erik Tilt [CodePlex #7944](https://phpexcel.codeplex.com/workitem/7944) -- Incomplete border style support in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7949](https://phpexcel.codeplex.com/workitem/7949) -- Conditional formatting "containsText" read/write results in MS Office Excel 2007 crash - @MarkBaker [CodePlex #7928](https://phpexcel.codeplex.com/workitem/7928) -- All sheets are always selected in output when using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7995](https://phpexcel.codeplex.com/workitem/7995) -- COLUMN function warning message during plain read/write - @MarkBaker [CodePlex #8013](https://phpexcel.codeplex.com/workitem/8013) -- setValue(0) results in string data type '0' - @MarkBaker [CodePlex #8155](https://phpexcel.codeplex.com/workitem/8155) -- Styles not removed when removing rows from sheet - @MarkBaker [CodePlex #8226](https://phpexcel.codeplex.com/workitem/8226) -- =IF formula causes fatal error during $objWriter->save() in Excel2007 format - @MarkBaker [CodePlex #8301](https://phpexcel.codeplex.com/workitem/8301) -- Exception thrown reading valid xls file: "Excel file is corrupt. Didn't find CONTINUE record while reading shared strings" - @Erik Tilt [CodePlex #8333](https://phpexcel.codeplex.com/workitem/8333) -- MS Outlook corrupts files generated by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8320](https://phpexcel.codeplex.com/workitem/8320) -- Undefined method PHPExcel_Worksheet::setFreezePane() in ReferenceHelper.php on line 271 - @MarkBaker [CodePlex #8351](https://phpexcel.codeplex.com/workitem/8351) -- Ampersands (&), left and right angles (<, >) in Rich-Text strings leads to corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8401](https://phpexcel.codeplex.com/workitem/8401) -- Print header and footer not supporting UTF-8 in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8408](https://phpexcel.codeplex.com/workitem/8408) -- Vertical page breaks not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8463](https://phpexcel.codeplex.com/workitem/8463) -- Missing support for accounting underline types in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8476](https://phpexcel.codeplex.com/workitem/8476) -- Infinite loops when reading corrupt xls file using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8482](https://phpexcel.codeplex.com/workitem/8482) -- Sheet protection password not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8566](https://phpexcel.codeplex.com/workitem/8566) -- PHPExcel_Style_NumberFormat::FORMAT_NUMBER ignored by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8596](https://phpexcel.codeplex.com/workitem/8596) -- PHPExcel_Reader_Excel5 fails a whole when workbook contains a chart - @Erik Tilt [CodePlex #8781](https://phpexcel.codeplex.com/workitem/8781) -- Occasional loss of column widths using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8788](https://phpexcel.codeplex.com/workitem/8788) -- Notices while reading formulas with deleted sheet references using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8795](https://phpexcel.codeplex.com/workitem/8795) -- Default style not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #8807](https://phpexcel.codeplex.com/workitem/8807) -- Blank rows occupy too much space in file generated by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9341](https://phpexcel.codeplex.com/workitem/9341) - -## [1.6.4] - 2008-10-27 - -### Features - -- RK record number error in MS developer documentation: 0x007E should be 0x027E - @Erik Tilt [CodePlex #7882](https://phpexcel.codeplex.com/workitem/7882) -- getHighestColumn() returning "@" for blank worksheet causes corrupt output - @MarkBaker [CodePlex #7878](https://phpexcel.codeplex.com/workitem/7878) -- Implement ROW and COLUMN Lookup/Reference Functions (when specified with a parameter) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement initial work on OFFSET Lookup/Reference Function (returning address rather than value at address) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Excel5 reader: Page margins - @Erik Tilt [CodePlex #7416](https://phpexcel.codeplex.com/workitem/7416) -- Excel5 reader: Header & Footer - @Erik Tilt [CodePlex #7417](https://phpexcel.codeplex.com/workitem/7417) -- Excel5 reader support for page setup (paper size etc.) - @Erik Tilt [CodePlex #7449](https://phpexcel.codeplex.com/workitem/7449) -- Improve speed and memory consumption of PHPExcel_Writer_CSV - @MarkBaker [CodePlex #7445](https://phpexcel.codeplex.com/workitem/7445) -- Better recognition of number format in HTML, CSV, and PDF writer - @MarkBaker [CodePlex #7432](https://phpexcel.codeplex.com/workitem/7432) -- Font support: Superscript and Subscript - @MarkBaker [CodePlex #7485](https://phpexcel.codeplex.com/workitem/7485) -- Excel5 reader font support: Super- and subscript - @Erik Tilt [CodePlex #7509](https://phpexcel.codeplex.com/workitem/7509) -- Excel5 reader style support: Text rotation and stacked text - @Erik Tilt [CodePlex #7521](https://phpexcel.codeplex.com/workitem/7521) -- Excel5 reader: Support for hyperlinks - @Erik Tilt [CodePlex #7530](https://phpexcel.codeplex.com/workitem/7530) -- Import sheet by request - @MB, ET [CodePlex #7557](https://phpexcel.codeplex.com/workitem/7557) -- PHPExcel_Reader_Excel5 support for page breaks - @Erik Tilt [CodePlex #7607](https://phpexcel.codeplex.com/workitem/7607) -- PHPExcel_Reader_Excel5 support for shrink-to-fit - @Erik Tilt [CodePlex #7622](https://phpexcel.codeplex.com/workitem/7622) -- Support for error types - @MB, ET [CodePlex #7675](https://phpexcel.codeplex.com/workitem/7675) -- Excel5 reader true formula support - @Erik Tilt [CodePlex #7388](https://phpexcel.codeplex.com/workitem/7388) -- Support for named ranges (defined names) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7701](https://phpexcel.codeplex.com/workitem/7701) -- Support for repeating rows and repeating columns (print titles) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7781](https://phpexcel.codeplex.com/workitem/7781) -- Support for print area in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7783](https://phpexcel.codeplex.com/workitem/7783) -- Excel5 reader and writer support for horizontal and vertical centering of page - @Erik Tilt [CodePlex #7795](https://phpexcel.codeplex.com/workitem/7795) -- Applied patch 1962 - @MarkBaker -- Excel5 reader and writer support for hidden cells (formulas) - @Erik Tilt [CodePlex #7866](https://phpexcel.codeplex.com/workitem/7866) -- Support for indentation in cells (feature request) - @MB, ET [CodePlex #7612](https://phpexcel.codeplex.com/workitem/7612) - -### Bugfixes - -- Option for reading only specified interval of rows in a sheet - @MB, ET [CodePlex #7828](https://phpexcel.codeplex.com/workitem/7828) -- PHPExcel_Calculation_Functions::DATETIMENOW() and PHPExcel_Calculation_Functions::DATENOW() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) -- Modified PHPExcel_Shared_Date::FormattedPHPToExcel() and PHPExcel_Shared_Date::ExcelToPHP to force datatype for return values - @MarkBaker [CodePlex #7395](https://phpexcel.codeplex.com/workitem/7395) -- Excel5 reader not producing UTF-8 strings with BIFF5 files - @Erik Tilt [CodePlex #7450](https://phpexcel.codeplex.com/workitem/7450) -- Array constant in formula gives run-time notice with Excel2007 writer - @MarkBaker [CodePlex #7470](https://phpexcel.codeplex.com/workitem/7470) -- PHPExcel_Reader_Excel2007 setReadDataOnly(true) returns Rich-Text - @MarkBaker [CodePlex #7494](https://phpexcel.codeplex.com/workitem/7494) -- PHPExcel_Reader_Excel5 setReadDataOnly(true) returns Rich-Text - @Erik Tilt [CodePlex #7496](https://phpexcel.codeplex.com/workitem/7496) -- Characters before superscript or subscript losing style - @MarkBaker [CodePlex #7497](https://phpexcel.codeplex.com/workitem/7497) -- Subscript not working with HTML writer - @MarkBaker [CodePlex #7507](https://phpexcel.codeplex.com/workitem/7507) -- DefaultColumnDimension not working on first column (A) - @MarkBaker [CodePlex #7508](https://phpexcel.codeplex.com/workitem/7508) -- Negative numbers are stored as text in PHPExcel_Writer_2007 - @MarkBaker [CodePlex #7527](https://phpexcel.codeplex.com/workitem/7527) -- Text rotation and stacked text not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7531](https://phpexcel.codeplex.com/workitem/7531) -- PHPExcel_Shared_Date::isDateTimeFormatCode erroneously says true - @MarkBaker [CodePlex #7536](https://phpexcel.codeplex.com/workitem/7536) -- Different images with same filename in separate directories become duplicates - @MarkBaker [CodePlex #7559](https://phpexcel.codeplex.com/workitem/7559) -- PHPExcel_Reader_Excel5 not returning sheet names as UTF-8 using for Excel 95 files - @Erik Tilt [CodePlex #7568](https://phpexcel.codeplex.com/workitem/7568) -- setAutoSize(true) on empty column gives column width of 10 using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7575](https://phpexcel.codeplex.com/workitem/7575) -- setAutoSize(true) on empty column gives column width of 255 using PHPExcel_Writer_Excel5 - @MB, ET [CodePlex #7573](https://phpexcel.codeplex.com/workitem/7573) -- Worksheet_Drawing bug - @MarkBaker [CodePlex #7514](https://phpexcel.codeplex.com/workitem/7514) -- getCalculatedValue() with REPT function causes script to stop - @MarkBaker [CodePlex #7593](https://phpexcel.codeplex.com/workitem/7593) -- getCalculatedValue() with LEN function causes script to stop - @MarkBaker [CodePlex #7594](https://phpexcel.codeplex.com/workitem/7594) -- Explicit fit-to-width (page setup) results in fit-to-height becoming 1 - @MarkBaker [CodePlex #7600](https://phpexcel.codeplex.com/workitem/7600) -- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet - @MarkBaker [CodePlex #7610](https://phpexcel.codeplex.com/workitem/7610) -- Conditional styles not read properly using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7516](https://phpexcel.codeplex.com/workitem/7516) -- PHPExcel_Writer_2007: Default worksheet style works only for first sheet - @MarkBaker [CodePlex #7611](https://phpexcel.codeplex.com/workitem/7611) -- Cannot Lock Cells using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #6940](https://phpexcel.codeplex.com/workitem/6940) -- Incorrect cell protection values found when using Excel5 reader - @Erik Tilt [CodePlex #7621](https://phpexcel.codeplex.com/workitem/7621) -- Default row height not working above highest row using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7623](https://phpexcel.codeplex.com/workitem/7623) -- Default column width does not get applied when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7637](https://phpexcel.codeplex.com/workitem/7637) -- Broken support for UTF-8 string formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7642](https://phpexcel.codeplex.com/workitem/7642) -- UTF-8 sheet names not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7643](https://phpexcel.codeplex.com/workitem/7643) -- getCalculatedValue() with ISNONTEXT function causes script to stop - @MarkBaker [CodePlex #7631](https://phpexcel.codeplex.com/workitem/7631) -- Missing BIFF3 functions in PHPExcel_Writer_Excel5: USDOLLAR (YEN), FINDB, SEARCHB, REPLACEB, LEFTB, RIGHTB, MIDB, LENB, ASC, DBCS (JIS) - @Erik Tilt [CodePlex #7652](https://phpexcel.codeplex.com/workitem/7652) -- Excel5 reader doesn't read numbers correctly in 64-bit systems - @Erik Tilt [CodePlex #7663](https://phpexcel.codeplex.com/workitem/7663) -- Missing BIFF5 functions in PHPExcel_Writer_Excel5: ISPMT, DATEDIF, DATESTRING, NUMBERSTRING - @Erik Tilt [CodePlex #7667](https://phpexcel.codeplex.com/workitem/7667) -- Missing BIFF8 functions in PHPExcel_Writer_Excel5: GETPIVOTDATA, HYPERLINK, PHONETIC, AVERAGEA, MAXA, MINA, STDEVPA, VARPA, STDEVA, VARA - @Erik Tilt [CodePlex #7668](https://phpexcel.codeplex.com/workitem/7668) -- Wrong host value in PHPExcel_Shared_ZipStreamWrapper::stream_open() - @MarkBaker [CodePlex #7657](https://phpexcel.codeplex.com/workitem/7657) -- PHPExcel_Reader_Excel5 not reading explicitly entered error types in cells - @Erik Tilt [CodePlex #7676](https://phpexcel.codeplex.com/workitem/7676) -- Boolean and error data types not preserved for formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7678](https://phpexcel.codeplex.com/workitem/7678) -- PHPExcel_Reader_Excel2007 ignores cell data type - @MarkBaker [CodePlex #7695](https://phpexcel.codeplex.com/workitem/7695) -- PHPExcel_Reader_Excel5 ignores cell data type - @Erik Tilt [CodePlex #7712](https://phpexcel.codeplex.com/workitem/7712) -- PHPExcel_Writer_Excel5 not aware of data type - @Erik Tilt [CodePlex #7587](https://phpexcel.codeplex.com/workitem/7587) -- Long strings sometimes truncated when using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7713](https://phpexcel.codeplex.com/workitem/7713) -- Direct entry of boolean or error type in cell not supported by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7727](https://phpexcel.codeplex.com/workitem/7727) -- PHPExcel_Reader_Excel2007: Error reading cell with data type string, date number format, and numeric-like cell value - @MarkBaker [CodePlex #7714](https://phpexcel.codeplex.com/workitem/7714) -- Row and column outlines (group indent level) not showing after using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7735](https://phpexcel.codeplex.com/workitem/7735) -- Missing UTF-8 support in number format codes for PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7737](https://phpexcel.codeplex.com/workitem/7737) -- Missing UTF-8 support with PHPExcel_Writer_Excel5 for explicit string in formula - @Erik Tilt [CodePlex #7750](https://phpexcel.codeplex.com/workitem/7750) -- Problem with class constants in PHPExcel_Style_NumberFormat - @MarkBaker [CodePlex #7726](https://phpexcel.codeplex.com/workitem/7726) -- Sometimes errors with PHPExcel_Reader_Excel5 reading hyperlinks - @Erik Tilt [CodePlex #7758](https://phpexcel.codeplex.com/workitem/7758) -- Hyperlink in cell always results in string data type when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7759](https://phpexcel.codeplex.com/workitem/7759) -- Excel file with blank sheet seen as broken in MS Office Excel 2007 when created by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7771](https://phpexcel.codeplex.com/workitem/7771) -- PHPExcel_Reader_Excel5: Incorrect reading of formula with explicit string containing (escaped) double-quote - @Erik Tilt [CodePlex #7785](https://phpexcel.codeplex.com/workitem/7785) -- getCalculatedValue() fails on formula with sheet name containing (escaped) single-quote - @MarkBaker [CodePlex #7787](https://phpexcel.codeplex.com/workitem/7787) -- getCalculatedValue() fails on formula with explicit string containing (escaped) double-quote - @MarkBaker [CodePlex #7786](https://phpexcel.codeplex.com/workitem/7786) -- Problems with simultaneous repeatRowsAtTop and repeatColumnsAtLeft using Excel2007 reader and writer - @MarkBaker [CodePlex #7780](https://phpexcel.codeplex.com/workitem/7780) -- PHPExcel_Reader_Excel5: Error reading formulas with sheet reference containing special characters - @Erik Tilt [CodePlex #7802](https://phpexcel.codeplex.com/workitem/7802) -- Off-sheet references sheet!A1 not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7831](https://phpexcel.codeplex.com/workitem/7831) -- Repeating rows/columns (print titles), print area not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7834](https://phpexcel.codeplex.com/workitem/7834) -- Formula having datetime number format shows as text when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7849](https://phpexcel.codeplex.com/workitem/7849) -- Cannot set formula to hidden using applyFromArray() - @MarkBaker [CodePlex #7863](https://phpexcel.codeplex.com/workitem/7863) -- HTML/PDF Writers limited to 26 columns by calculateWorksheetDimension (erroneous comparison in getHighestColumn() method) - @MarkBaker [CodePlex #7805](https://phpexcel.codeplex.com/workitem/7805) -- Formula returning error type is lost when read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7873](https://phpexcel.codeplex.com/workitem/7873) -- PHPExcel_Reader_Excel5: Cell style lost for last column in group of blank cells - @Erik Tilt [CodePlex #7883](https://phpexcel.codeplex.com/workitem/7883) -- Column width sometimes collapses to auto size using Excel2007 reader/writer - @MarkBaker [CodePlex #7886](https://phpexcel.codeplex.com/workitem/7886) -- Data Validation Formula = 0 crashes Excel - @MarkBaker [CodePlex #9343](https://phpexcel.codeplex.com/workitem/9343) - -## [1.6.3] - 2008-08-25 - -### General - -- Modified PHPExcel_Shared_Date::PHPToExcel() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367) -- Applied patch 1629 - @MarkBaker -- Applied patch 1644 - @MarkBaker -- Implement repeatRow and repeatColumn in Excel5 writer - @MarkBaker [CodePlex #6485](https://phpexcel.codeplex.com/workitem/6485) - -### Features - -- Remove scene3d filter in Excel2007 drawing - @MarkBaker [CodePlex #6838](https://phpexcel.codeplex.com/workitem/6838) -- Implement CHOOSE and INDEX Lookup/Reference Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement CLEAN Text Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement YEARFRAC Date/Time Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement 2 options for print/show gridlines - @MarkBaker [CodePlex #6508](https://phpexcel.codeplex.com/workitem/6508) -- Add VLOOKUP function (contribution) - @MarkBaker [CodePlex #7270](https://phpexcel.codeplex.com/workitem/7270) -- Implemented: ShrinkToFit - @MarkBaker [CodePlex #7182](https://phpexcel.codeplex.com/workitem/7182) -- Row heights not updated correctly when inserting new rows - @MarkBaker [CodePlex #7218](https://phpexcel.codeplex.com/workitem/7218) -- Copy worksheets within the same workbook - @MarkBaker [CodePlex #7157](https://phpexcel.codeplex.com/workitem/7157) -- Excel5 reader style support: horizontal and vertical alignment plus text wrap - @Erik Tilt [CodePlex #7290](https://phpexcel.codeplex.com/workitem/7290) -- Excel5 reader support for merged cells - @Erik Tilt [CodePlex #7294](https://phpexcel.codeplex.com/workitem/7294) -- Excel5 reader: Sheet Protection - @Erik Tilt [CodePlex #7296](https://phpexcel.codeplex.com/workitem/7296) -- Excel5 reader: Password for sheet protection - @Erik Tilt [CodePlex #7297](https://phpexcel.codeplex.com/workitem/7297) -- Excel5 reader: Column width - @Erik Tilt [CodePlex #7299](https://phpexcel.codeplex.com/workitem/7299) -- Excel5 reader: Row height - @Erik Tilt [CodePlex #7301](https://phpexcel.codeplex.com/workitem/7301) -- Excel5 reader: Font support - @Erik Tilt [CodePlex #7304](https://phpexcel.codeplex.com/workitem/7304) -- Excel5 reader: support for locked cells - @Erik Tilt [CodePlex #7324](https://phpexcel.codeplex.com/workitem/7324) -- Excel5 reader style support: Fill (background colors and patterns) - @Erik Tilt [CodePlex #7330](https://phpexcel.codeplex.com/workitem/7330) -- Excel5 reader style support: Borders (style and color) - @Erik Tilt [CodePlex #7332](https://phpexcel.codeplex.com/workitem/7332) -- Excel5 reader: Rich-Text support - @Erik Tilt [CodePlex #7346](https://phpexcel.codeplex.com/workitem/7346) -- Read Excel built-in number formats with Excel 2007 reader - @MarkBaker [CodePlex #7313](https://phpexcel.codeplex.com/workitem/7313) -- Excel5 reader: Number format support - @Erik Tilt [CodePlex #7317](https://phpexcel.codeplex.com/workitem/7317) -- Creating a copy of PHPExcel object - @MarkBaker [CodePlex #7362](https://phpexcel.codeplex.com/workitem/7362) -- Excel5 reader: support for row / column outline (group) - @Erik Tilt [CodePlex #7373](https://phpexcel.codeplex.com/workitem/7373) -- Implement default row/column sizes - @MarkBaker [CodePlex #7380](https://phpexcel.codeplex.com/workitem/7380) -- Writer HTML - option to return styles and table separately - @MarkBaker [CodePlex #7364](https://phpexcel.codeplex.com/workitem/7364) - -### Bugfixes - -- Excel5 reader: Support for remaining built-in number formats - @Erik Tilt [CodePlex #7393](https://phpexcel.codeplex.com/workitem/7393) -- Fixed rounding in HOUR MINUTE and SECOND Time functions, and improved performance for these - @MarkBaker -- Fix to TRIM function - @MarkBaker -- Fixed range validation in TIME Functions.php - @MarkBaker -- EDATE and EOMONTH functions now return date values based on the returnDateType flag - @MarkBaker -- Write date values that are the result of a calculation function correctly as Excel serialized dates rather than PHP serialized date values - @MarkBaker -- Excel2007 reader not always reading boolean correctly - @MarkBaker [CodePlex #6690](https://phpexcel.codeplex.com/workitem/6690) -- Columns above IZ - @MarkBaker [CodePlex #6275](https://phpexcel.codeplex.com/workitem/6275) -- Other locale than English causes Excel2007 writer to produce broken xlsx - @MarkBaker [CodePlex #6853](https://phpexcel.codeplex.com/workitem/6853) -- Typo: Number_fromat in NumberFormat.php - @MarkBaker [CodePlex #7061](https://phpexcel.codeplex.com/workitem/7061) -- Bug in Worksheet_BaseDrawing setWidth() - @MarkBaker [CodePlex #6865](https://phpexcel.codeplex.com/workitem/6865) -- PDF writer collapses column width for merged cells - @MarkBaker [CodePlex #6891](https://phpexcel.codeplex.com/workitem/6891) -- Issues with drawings filenames - @MarkBaker [CodePlex #6867](https://phpexcel.codeplex.com/workitem/6867) -- fromArray() local variable isn't defined - @MarkBaker [CodePlex #7073](https://phpexcel.codeplex.com/workitem/7073) -- PHPExcel_Writer_Excel5->setTempDir() not passed to all classes involved in writing to a file - @MarkBaker [CodePlex #7276](https://phpexcel.codeplex.com/workitem/7276) -- Excel5 reader not handling UTF-8 properly - @MarkBaker [CodePlex #7277](https://phpexcel.codeplex.com/workitem/7277) -- If you write a 0 value in cell, cell shows as empty - @MarkBaker [CodePlex #7327](https://phpexcel.codeplex.com/workitem/7327) -- Excel2007 writer: Row height ignored for empty rows - @MarkBaker [CodePlex #7302](https://phpexcel.codeplex.com/workitem/7302) -- Excel2007 (comments related error) - @MarkBaker [CodePlex #7281](https://phpexcel.codeplex.com/workitem/7281) -- Column width in other locale - @MarkBaker [CodePlex #7345](https://phpexcel.codeplex.com/workitem/7345) -- Excel2007 reader not reading underlined Rich-Text - @MarkBaker [CodePlex #7347](https://phpexcel.codeplex.com/workitem/7347) -- Excel5 reader converting booleans to strings - @Erik Tilt [CodePlex #7357](https://phpexcel.codeplex.com/workitem/7357) -- Recursive Object Memory Leak - @MarkBaker [CodePlex #7365](https://phpexcel.codeplex.com/workitem/7365) -- Excel2007 writer ignoring row dimensions without cells - @MarkBaker [CodePlex #7372](https://phpexcel.codeplex.com/workitem/7372) -- Excel5 reader is converting formatted numbers / dates to strings - @Erik Tilt [CodePlex #7382](https://phpexcel.codeplex.com/workitem/7382) - -## [1.6.2] - 2008-06-23 - -### General - -- Document style array values - @MarkBaker [CodePlex #6088](https://phpexcel.codeplex.com/workitem/6088) -- Applied patch 1195 - @MarkBaker -- Redirecting output to a client’s web browser - http headers - @MarkBaker [CodePlex #6178](https://phpexcel.codeplex.com/workitem/6178) -- Improve worksheet garbage collection - @MarkBaker [CodePlex #6187](https://phpexcel.codeplex.com/workitem/6187) -- Functions that return date values can now be configured to return as Excel serialized date/time, PHP serialized date/time, or a PHP date/time object. - @MarkBaker -- Functions that explicitly accept dates as parameters now permit values as Excel serialized date/time, PHP serialized date/time, a valid date string, or a PHP date/time object. - @MarkBaker -- Implement ACOSH, ASINH and ATANH functions for those operating platforms/PHP versions that don't include these functions - @MarkBaker -- Implement ATAN2 logic reversing the arguments as per Excel - @MarkBaker -- Additional validation of parameters for COMBIN - @MarkBaker - -### Features - -- Fixed validation for CEILING and FLOOR when the value and significance parameters have different signs; and allowed default value of 1 or -1 for significance when in GNUMERIC compatibility mode - @MarkBaker -- Implement ADDRESS, ISLOGICAL, ISTEXT and ISNONTEXT functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement COMPLEX, IMAGINARY, IMREAL, IMARGUMENT, IMCONJUGATE, IMABS, IMSUB, IMDIV, IMSUM, IMPRODUCT, IMSQRT, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER IMCOS and IMSIN Engineering functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Implement NETWORKDAYS and WORKDAY Date/Time functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) -- Make cell column AAA available - @MarkBaker [CodePlex #6100](https://phpexcel.codeplex.com/workitem/6100) -- Mark particular cell as selected when opening Excel - @MarkBaker [CodePlex #6095](https://phpexcel.codeplex.com/workitem/6095) -- Multiple sheets in PDF and HTML - @MarkBaker [CodePlex #6120](https://phpexcel.codeplex.com/workitem/6120) -- Implement PHPExcel_ReaderFactory and PHPExcel_WriterFactory - @MarkBaker [CodePlex #6227](https://phpexcel.codeplex.com/workitem/6227) -- Set image root of PHPExcel_Writer_HTML - @MarkBaker [CodePlex #6249](https://phpexcel.codeplex.com/workitem/6249) -- Enable/disable calculation cache - @MarkBaker [CodePlex #6264](https://phpexcel.codeplex.com/workitem/6264) -- PDF writer and multi-line text - @MarkBaker [CodePlex #6259](https://phpexcel.codeplex.com/workitem/6259) -- Feature request - setCacheExpirationTime() - @MarkBaker [CodePlex #6350](https://phpexcel.codeplex.com/workitem/6350) -- Implement late-binding mechanisms to reduce memory footprint - @JB [CodePlex #6370](https://phpexcel.codeplex.com/workitem/6370) -- Implement shared styles - @JB [CodePlex #6430](https://phpexcel.codeplex.com/workitem/6430) -- Copy sheet from external Workbook to active Workbook - @MarkBaker [CodePlex #6391](https://phpexcel.codeplex.com/workitem/6391) - -### Bugfixes - -- Functions in Conditional Formatting - @MarkBaker [CodePlex #6428](https://phpexcel.codeplex.com/workitem/6428) -- Default Style in Excel5 - @MarkBaker [CodePlex #6096](https://phpexcel.codeplex.com/workitem/6096) -- Numbers starting with '+' cause Excel 2007 errors - @MarkBaker [CodePlex #6150](https://phpexcel.codeplex.com/workitem/6150) -- ExcelWriter5 is not PHP5 compatible, using it with E_STRICT results in a bunch of errors (applied patches) - @MarkBaker [CodePlex #6092](https://phpexcel.codeplex.com/workitem/6092) -- Error Reader Excel2007 line 653 foreach ($relsDrawing->Relationship as $ele) - @MarkBaker [CodePlex #6179](https://phpexcel.codeplex.com/workitem/6179) -- Worksheet toArray() screws up DATE - @MarkBaker [CodePlex #6229](https://phpexcel.codeplex.com/workitem/6229) -- References to a Richtext cell in a formula - @MarkBaker [CodePlex #6253](https://phpexcel.codeplex.com/workitem/6253) -- insertNewColumnBefore Bug - @MarkBaker [CodePlex #6285](https://phpexcel.codeplex.com/workitem/6285) -- Error reading Excel2007 file with shapes - @MarkBaker [CodePlex #6319](https://phpexcel.codeplex.com/workitem/6319) -- Determine whether date values need conversion from PHP dates to Excel dates before writing to file, based on the data type (float or integer) - @MarkBaker [CodePlex #6302](https://phpexcel.codeplex.com/workitem/6302) -- Fixes to DATE function when it is given negative input parameters - @MarkBaker -- PHPExcel handles empty cells other than Excel - @MarkBaker [CodePlex #6347](https://phpexcel.codeplex.com/workitem/6347) -- PHPExcel handles 0 and "" as being the same - @MarkBaker [CodePlex #6348](https://phpexcel.codeplex.com/workitem/6348) -- Problem Using Excel2007 Reader for Spreadsheets containing images - @MarkBaker [CodePlex #6357](https://phpexcel.codeplex.com/workitem/6357) -- ShowGridLines ignored when reading/writing Excel 2007 - @MarkBaker [CodePlex #6359](https://phpexcel.codeplex.com/workitem/6359) -- Bug With Word Wrap in Excel 2007 Reader - @MarkBaker [CodePlex #6426](https://phpexcel.codeplex.com/workitem/6426) - -## [1.6.1] - 2008-04-28 - -### General - -- Fix documentation printing - @MarkBaker [CodePlex #5532](https://phpexcel.codeplex.com/workitem/5532) -- Memory usage improvements - @MarkBaker [CodePlex #5586](https://phpexcel.codeplex.com/workitem/5586) -- Applied patch 990 - @MarkBaker - -### Features - -- Applied patch 991 - @MarkBaker -- Implement PHPExcel_Reader_Excel5 - @BM [CodePlex #2841](https://phpexcel.codeplex.com/workitem/2841) -- Implement "toArray" and "fromArray" method - @MarkBaker [CodePlex #5564](https://phpexcel.codeplex.com/workitem/5564) -- Read shared formula - @MarkBaker [CodePlex #5665](https://phpexcel.codeplex.com/workitem/5665) -- Read image twoCellAnchor - @MarkBaker [CodePlex #5681](https://phpexcel.codeplex.com/workitem/5681) -- &G Image as bg for headerfooter - @MarkBaker [CodePlex #4446](https://phpexcel.codeplex.com/workitem/4446) -- Implement page layout functionality for Excel5 format - @MarkBaker [CodePlex #5834](https://phpexcel.codeplex.com/workitem/5834) - -### Bugfixes - -- Feature request: PHPExcel_Writer_PDF - @MarkBaker [CodePlex #6039](https://phpexcel.codeplex.com/workitem/6039) -- DefinedNames null check - @MarkBaker [CodePlex #5517](https://phpexcel.codeplex.com/workitem/5517) -- Hyperlinks should not always have trailing slash - @MarkBaker [CodePlex #5463](https://phpexcel.codeplex.com/workitem/5463) -- Saving Error - Uncaught exception (#REF! named range) - @MarkBaker [CodePlex #5592](https://phpexcel.codeplex.com/workitem/5592) -- Error when creating Zip file on Linux System (Not Windows) - @MarkBaker [CodePlex #5634](https://phpexcel.codeplex.com/workitem/5634) -- Time incorrecly formated - @MarkBaker [CodePlex #5876](https://phpexcel.codeplex.com/workitem/5876) -- Conditional formatting - second rule not applied - @MarkBaker [CodePlex #5914](https://phpexcel.codeplex.com/workitem/5914) -- PHPExcel_Reader_Excel2007 cannot load PHPExcel_Shared_File - @MarkBaker [CodePlex #5978](https://phpexcel.codeplex.com/workitem/5978) -- Output redirection to web browser - @MarkBaker [CodePlex #6020](https://phpexcel.codeplex.com/workitem/6020) - -## [1.6.0] - 2008-02-14 - -### Features - -- Use PHPExcel datatypes in formula calculation - @MarkBaker [CodePlex #3156](https://phpexcel.codeplex.com/workitem/3156) -- Center on page when printing - @MarkBaker [CodePlex #5019](https://phpexcel.codeplex.com/workitem/5019) -- Hyperlink to other spreadsheet - @MarkBaker [CodePlex #5099](https://phpexcel.codeplex.com/workitem/5099) -- Set the print area of a worksheet - @MarkBaker [CodePlex #5104](https://phpexcel.codeplex.com/workitem/5104) -- Read "definedNames" property of worksheet - @MarkBaker [CodePlex #5118](https://phpexcel.codeplex.com/workitem/5118) -- Set default style for all cells - @MarkBaker [CodePlex #5338](https://phpexcel.codeplex.com/workitem/5338) -- Named Ranges - @MarkBaker [CodePlex #4216](https://phpexcel.codeplex.com/workitem/4216) - -### Bugfixes - -- Implement worksheet references (Sheet1!A1) - @MarkBaker [CodePlex #5398](https://phpexcel.codeplex.com/workitem/5398) -- Redirect output to a client's web browser - @MarkBaker [CodePlex #4967](https://phpexcel.codeplex.com/workitem/4967) -- "File Error: data may have been lost." seen in Excel 2007 and Excel 2003 SP3 when opening XLS file - @MarkBaker [CodePlex #5008](https://phpexcel.codeplex.com/workitem/5008) -- Bug in style's getHashCode() - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) -- PHPExcel_Reader not correctly reading numeric values - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165) -- Text rotation is read incorrectly - @MarkBaker [CodePlex #5324](https://phpexcel.codeplex.com/workitem/5324) -- Enclosure " and data " result a bad data : \" instead of "" - @MarkBaker [CodePlex #5326](https://phpexcel.codeplex.com/workitem/5326) -- Formula parser - IF statement returning array instead of scalar - @MarkBaker [CodePlex #5332](https://phpexcel.codeplex.com/workitem/5332) -- setFitToWidth(nbpage) & setFitToWidth(nbpage) work partially - @MarkBaker [CodePlex #5351](https://phpexcel.codeplex.com/workitem/5351) -- Worksheet::setTitle() causes unwanted renaming - @MarkBaker [CodePlex #5361](https://phpexcel.codeplex.com/workitem/5361) -- Hyperlinks not working. Results in broken xlsx file. - @MarkBaker [CodePlex #5407](https://phpexcel.codeplex.com/workitem/5407) - -## [1.5.5] - 2007-12-24 - -### General - -- Grouping Rows - @MarkBaker [CodePlex #4135](https://phpexcel.codeplex.com/workitem/4135) - -### Features - -- Semi-nightly builds - @MarkBaker [CodePlex #4427](https://phpexcel.codeplex.com/workitem/4427) -- Implement "date" datatype - @MarkBaker [CodePlex #3155](https://phpexcel.codeplex.com/workitem/3155) -- Date format not honored in CSV writer - @MarkBaker [CodePlex #4150](https://phpexcel.codeplex.com/workitem/4150) -- RichText and sharedStrings - @MarkBaker [CodePlex #4199](https://phpexcel.codeplex.com/workitem/4199) -- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of DATE, DATEDIF, DATEVALUE, DAY, DAYS360- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of AVEDEV, HARMEAN and GEOMEAN - - Addition of the BINOMDIST (Non-cumulative only), COUNTBLANK, EXPONDIST, FISHER, FISHERINV, NORMDIST, NORMSDIST, PERMUT, POISSON (Non-cumulative only) and STANDARDIZE Statistical Functions - - Addition of the CEILING, COMBIN, EVEN, FACT, FACTDOUBLE, FLOOR, MULTINOMIAL, ODD, ROUNDDOWN, ROUNDUP, SIGN, SQRTPI and SUMSQ Mathematical Functions - - Addition of the NORMINV, NORMSINV, CONFIDENCE and SKEW Statistical Functions - - Addition of the CRITBINOM, HYPGEOMDIST, KURT, LOGINV, LOGNORMDIST, NEGBINOMDIST and WEIBULL Statistical Functions - - Addition of the LARGE, PERCENTILE, QUARTILE, SMALL and TRIMMEAN Statistical Functions - - Addition of the BIN2HEX, BIN2OCT, DELTA, ERF, ERFC, GESTEP, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN and OCT2HEX Engineering Functions - - Addition of the CHIDIST, GAMMADIST and GAMMALN Statistical Functions - - Addition of the GCD, LCM, MROUND and SUBTOTAL Mathematical Functions - - Addition of the LOWER, PROPER and UPPER Text Functions - - Addition of the BETADIST and BETAINV Statistical Functions - - Addition of the CHIINV and GAMMAINV Statistical Functions - - Addition of the SERIESSUM Mathematical Function - - Addition of the CHAR, CODE, FIND, LEN, REPT, SEARCH, T, TRIM Text Functions - - Addition of the FALSE and TRUE Boolean Functions - - Addition of the TDIST and TINV Statistical Functions - - Addition of the EDATE, EOMONTH, YEAR, MONTH, TIME, TIMEVALUE, HOUR, MINUTE, SECOND, WEEKDAY, WEEKNUM, NOW, TODAY and Date/Time Function - - Addition of the BESSELI, BESSELJ, BESSELK and BESSELY Engineering Functions - - Addition of the SLN and SYD Financial Functions - - reworked MODE calculation to handle floating point numbers - - Improved error trapping for invalid input values - - Fix to SMALL, LARGE, PERCENTILE and TRIMMEAN to eliminate non-numeric values - - Added CDF to BINOMDIST and POISSON - - Fix to a potential endless loop in CRITBINOM, together with other bugfixes to the algorithm - - Fix to SQRTPI so that it will work with a real value parameter rather than just integers - - Trap for passing negative values to FACT - - Improved accuracy of the NORMDIST cumulative function, and of the ERF and ERFC functions - - Replicated Excel data-type and error handling for BIN, DEC, OCT and HEX conversion functions - - Replicated Excel data-type and error handling for AND and OR Boolean functions - - Bugfix to MROUND - - Rework of the DATE, DATEVALUE, DAY, DAYS360 and DATEDIF date/Time functions to use Excel dates rather than straight PHP dates - - Rework of the AND, OR Boolean functions to ignore string values - - Rework of the BIN2DEC, BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT Engineering functions to handle two's complement - - Excel, Gnumeric and OpenOffice Calc compatibility flag for functions - - Note, not all functions have yet been written to work with the Gnumeric and OpenOffice Calc compatibility flags - - 1900 or 1904 Calendar flag for date functions - - Reworked ExcelToPHP date method to handle the Excel 1900 leap year - - Note that this will not correctly return values prior to 13-Dec-1901 20:45:52 as this is the minimum value that PHP date serial values can handle. If you need to work with dates prior to this, then an ExcelToPHPObject method has been added which will work correctly with values between Excel's 1900 calendar base date of 1-Jan-1900, and 13-Dec-1901 - - Addition of ExcelToPHPObject date method to return a PHP DateTime object from an Excel date serial value - - PHPToExcel method modified to accept either PHP date serial numbers or PHP DateTime objects - - Addition of FormattedPHPToExcel which will accept a date and time broken to into year, month, day, hour, minute, second and return an Excel date serial value- Control characters in Excel 2007 - @MarkBaker [CodePlex #4485](https://phpexcel.codeplex.com/workitem/4485) -- BaseDrawing::setWidthAndHeight method request - @MarkBaker [CodePlex #4796](https://phpexcel.codeplex.com/workitem/4796) -- Page Setup -> Print Titles -> Sheet -> 'Rows to repeat at top' - @MarkBaker [CodePlex #4798](https://phpexcel.codeplex.com/workitem/4798) - -### Bugfixes - -- Comment functionality - @MarkBaker [CodePlex #4433](https://phpexcel.codeplex.com/workitem/4433) -- Undefined variable in PHPExcel_Writer_Serialized - @MarkBaker [CodePlex #4124](https://phpexcel.codeplex.com/workitem/4124) -- Notice: Object of class PHPExcel_RichText could not be converted to int - @MarkBaker [CodePlex #4125](https://phpexcel.codeplex.com/workitem/4125) -- Excel5Writer: utf8 string not converted to utf16 - @MarkBaker [CodePlex #4126](https://phpexcel.codeplex.com/workitem/4126) -- PHPExcel_RichText and autosize - @MarkBaker [CodePlex #4180](https://phpexcel.codeplex.com/workitem/4180) -- Excel5Writer produces broken xls files after change mentioned in work item 4126 - @MarkBaker [CodePlex #4574](https://phpexcel.codeplex.com/workitem/4574) -- Small bug in PHPExcel_Reader_Excel2007 function _readStyle - @MarkBaker [CodePlex #4797](https://phpexcel.codeplex.com/workitem/4797) - -## [1.5.0] - 2007-10-23 - -### Features - -- Refactor PHPExcel Drawing - @MarkBaker [CodePlex #3265](https://phpexcel.codeplex.com/workitem/3265) -- Update Shared/OLE.php to latest version from PEAR - @CS [CodePlex #3079](https://phpexcel.codeplex.com/workitem/3079) -- Excel2007 vs Excel2003 compatibility pack - @MarkBaker [CodePlex #3217](https://phpexcel.codeplex.com/workitem/3217) -- Cell protection (lock/unlock) - @MarkBaker [CodePlex #3234](https://phpexcel.codeplex.com/workitem/3234) -- Create clickable links (hyperlinks) - @MarkBaker [CodePlex #3543](https://phpexcel.codeplex.com/workitem/3543) -- Additional page setup parameters - @MarkBaker [CodePlex #3241](https://phpexcel.codeplex.com/workitem/3241) -- Make temporary file path configurable (Excel5) - @MarkBaker [CodePlex #3300](https://phpexcel.codeplex.com/workitem/3300) -- Small addition to applyFromArray for font - @MarkBaker [CodePlex #3306](https://phpexcel.codeplex.com/workitem/3306) - -### Bugfixes - -- Better feedback when save of file is not possible - @MarkBaker [CodePlex #3373](https://phpexcel.codeplex.com/workitem/3373) -- Text Rotation - @MarkBaker [CodePlex #3181](https://phpexcel.codeplex.com/workitem/3181) -- Small bug in Page Orientation - @MarkBaker [CodePlex #3237](https://phpexcel.codeplex.com/workitem/3237) -- insertNewColumnBeforeByColumn undefined - @MarkBaker [CodePlex #3812](https://phpexcel.codeplex.com/workitem/3812) -- Sheet references not working in formula (Excel5 Writer) - @MarkBaker [CodePlex #3893](https://phpexcel.codeplex.com/workitem/3893) - -## [1.4.5] - 2007-08-23 - -### General - -- Class file endings - @MarkBaker [CodePlex #3003](https://phpexcel.codeplex.com/workitem/3003) -- Different calculation engine improvements - @MarkBaker [CodePlex #3081](https://phpexcel.codeplex.com/workitem/3081) -- Different improvements in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #3082](https://phpexcel.codeplex.com/workitem/3082) - -### Features - -- Set XML indentation in PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #3146](https://phpexcel.codeplex.com/workitem/3146) -- Optionally store temporary Excel2007 writer data in file instead of memory - @MarkBaker [CodePlex #3159](https://phpexcel.codeplex.com/workitem/3159) -- Implement show/hide gridlines - @MarkBaker [CodePlex #3063](https://phpexcel.codeplex.com/workitem/3063) -- Implement option to read only data - @MarkBaker [CodePlex #3064](https://phpexcel.codeplex.com/workitem/3064) -- Optionally disable formula precalculation - @MarkBaker [CodePlex #3080](https://phpexcel.codeplex.com/workitem/3080) -- Explicitly set cell datatype - @MarkBaker [CodePlex #3154](https://phpexcel.codeplex.com/workitem/3154) - -### Bugfixes - -- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346) - - Addition of MINA, MAXA, COUNTA, AVERAGEA, MEDIAN, MODE, DEVSQ, STDEV, STDEVA, STDEVP, STDEVPA, VAR, VARA, VARP and VARPA Excel Functions - - Fix to SUM, PRODUCT, QUOTIENT, MIN, MAX, COUNT and AVERAGE functions when cell contains a numeric value in a string datatype, bringing it in line with MS Excel behaviour- File_exists on ZIP fails on some installations - @MarkBaker [CodePlex #2881](https://phpexcel.codeplex.com/workitem/2881) -- Argument in textRotation should be -90..90 - @MarkBaker [CodePlex #2879](https://phpexcel.codeplex.com/workitem/2879) -- Excel2007 reader/writer not implementing OpenXML/SpreadsheetML styles 100% correct - @MarkBaker [CodePlex #2883](https://phpexcel.codeplex.com/workitem/2883) -- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) -- Print and print preview of generated XLSX causes Excel2007 to crash - @MarkBaker [CodePlex #2935](https://phpexcel.codeplex.com/workitem/2935) -- Error in Calculations - COUNT() function - @MarkBaker [CodePlex #2952](https://phpexcel.codeplex.com/workitem/2952) -- HTML and CSV writer not writing last row - @MarkBaker [CodePlex #3002](https://phpexcel.codeplex.com/workitem/3002) -- Memory leak in Excel5 writer - @MarkBaker [CodePlex #3017](https://phpexcel.codeplex.com/workitem/3017) -- Printing (PHPExcel_Writer_Excel5) - @MarkBaker [CodePlex #3044](https://phpexcel.codeplex.com/workitem/3044) -- Problems reading zip:// - @MarkBaker [CodePlex #3046](https://phpexcel.codeplex.com/workitem/3046) -- Error reading conditional formatting - @MarkBaker [CodePlex #3047](https://phpexcel.codeplex.com/workitem/3047) -- Bug in Excel5 writer (storePanes) - @MarkBaker [CodePlex #3067](https://phpexcel.codeplex.com/workitem/3067) -- Memory leak in PHPExcel_Style_Color - @MarkBaker [CodePlex #3077](https://phpexcel.codeplex.com/workitem/3077) - -## [1.4.0] - 2007-07-23 - -### General - -- Coding convention / code cleanup - @MarkBaker [CodePlex #2687](https://phpexcel.codeplex.com/workitem/2687) -- Use set_include_path in tests - @MarkBaker [CodePlex #2717](https://phpexcel.codeplex.com/workitem/2717) - -### Features - -- Move PHPExcel_Writer_Excel5 OLE to PHPExcel_Shared_OLE - @MarkBaker [CodePlex #2812](https://phpexcel.codeplex.com/workitem/2812) -- Hide/Unhide Column or Row - @MarkBaker [CodePlex #2679](https://phpexcel.codeplex.com/workitem/2679) -- Implement multi-cell styling - @MarkBaker [CodePlex #2271](https://phpexcel.codeplex.com/workitem/2271) -- Implement CSV file format (reader/writer) - @MarkBaker [CodePlex #2720](https://phpexcel.codeplex.com/workitem/2720) - -### Bugfixes - -- Implement HTML file format - @MarkBaker [CodePlex #2845](https://phpexcel.codeplex.com/workitem/2845) -- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513) -- Freeze Panes with PHPExcel_Writer_Excel5 - @MarkBaker [CodePlex #2678](https://phpexcel.codeplex.com/workitem/2678) -- OLE.php - @MarkBaker [CodePlex #2680](https://phpexcel.codeplex.com/workitem/2680) -- Copy and pasting multiple drop-down list cells breaks reader - @MarkBaker [CodePlex #2736](https://phpexcel.codeplex.com/workitem/2736) -- Function setAutoFilterByColumnAndRow takes wrong arguments - @MarkBaker [CodePlex #2775](https://phpexcel.codeplex.com/workitem/2775) -- Simplexml_load_file fails on ZipArchive - @MarkBaker [CodePlex #2858](https://phpexcel.codeplex.com/workitem/2858) - -## [1.3.5] - 2007-06-27 - -### Features - -- Documentation - @MarkBaker [CodePlex #15](https://phpexcel.codeplex.com/workitem/15) -- PHPExcel_Writer_Excel5 - @JV -- PHPExcel_Reader_Excel2007: Image shadows - @JV -- Data validation - @MarkBaker [CodePlex #2385](https://phpexcel.codeplex.com/workitem/2385) - -### Bugfixes - -- Implement richtext strings - @MarkBaker -- Empty relations when adding image to any sheet but the first one - @MarkBaker [CodePlex #2443](https://phpexcel.codeplex.com/workitem/2443) -- Excel2007 crashes on print preview - @MarkBaker [CodePlex #2536](https://phpexcel.codeplex.com/workitem/2536) - -## [1.3.0] - 2007-06-05 - -### General - -- Create PEAR package - @MarkBaker [CodePlex #1942](https://phpexcel.codeplex.com/workitem/1942) - -### Features - -- Replace *->duplicate() by __clone() - @MarkBaker [CodePlex #2331](https://phpexcel.codeplex.com/workitem/2331) -- PHPExcel_Reader_Excel2007: Column auto-size, Protection, Merged cells, Wrap text, Page breaks, Auto filter, Images - @JV -- Implement "freezing" panes - @MarkBaker [CodePlex #245](https://phpexcel.codeplex.com/workitem/245) -- Cell addressing alternative - @MarkBaker [CodePlex #2273](https://phpexcel.codeplex.com/workitem/2273) -- Implement cell word-wrap attribute - @MarkBaker [CodePlex #2270](https://phpexcel.codeplex.com/workitem/2270) -- Auto-size column - @MarkBaker [CodePlex #2282](https://phpexcel.codeplex.com/workitem/2282) -- Implement formula calculation - @MarkBaker [CodePlex #241](https://phpexcel.codeplex.com/workitem/241) - -### Bugfixes - -- Insert/remove row/column - @MarkBaker [CodePlex #2375](https://phpexcel.codeplex.com/workitem/2375) -- PHPExcel_Worksheet::getCell() should not accept absolute coordinates - @MarkBaker [CodePlex #1931](https://phpexcel.codeplex.com/workitem/1931) -- Cell reference without row number - @MarkBaker [CodePlex #2272](https://phpexcel.codeplex.com/workitem/2272) -- Styles with same coordinate but different worksheet - @MarkBaker [CodePlex #2276](https://phpexcel.codeplex.com/workitem/2276) -- PHPExcel_Worksheet->getCellCollection() usort error - @MarkBaker [CodePlex #2290](https://phpexcel.codeplex.com/workitem/2290) -- Bug in PHPExcel_Cell::stringFromColumnIndex - @SS [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) -- Reader: numFmts can be missing, use cellStyleXfs instead of cellXfs in styles - @JV [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353) - -## [1.2.0] - 2007-04-26 - -### General - -- Stringtable attribute "count" not necessary, provides wrong info to Excel sometimes... - @MarkBaker -- Updated tests to address more document properties - @MarkBaker -- Some refactoring in PHPExcel_Writer_Excel2007_Workbook - @MarkBaker -- New package: PHPExcel_Shared - @MarkBaker -- Password hashing algorithm implemented in PHPExcel_Shared_PasswordHasher - @MarkBaker -- Moved pixel conversion functions to PHPExcel_Shared_Drawing - @MarkBaker -- Switch over to LGPL license - @MarkBaker [CodePlex #244](https://phpexcel.codeplex.com/workitem/244) - -### Features - -- Include PHPExcel version in file headers - @MarkBaker [CodePlex #5](https://phpexcel.codeplex.com/workitem/5) -- Autofilter - @MarkBaker [CodePlex #6](https://phpexcel.codeplex.com/workitem/6) -- Extra document property: keywords - @MarkBaker [CodePlex #7](https://phpexcel.codeplex.com/workitem/7) -- Extra document property: category - @MarkBaker [CodePlex #8](https://phpexcel.codeplex.com/workitem/8) -- Document security - @MarkBaker [CodePlex #9](https://phpexcel.codeplex.com/workitem/9) -- PHPExcel_Writer_Serialized and PHPExcel_Reader_Serialized - @MarkBaker [CodePlex #10](https://phpexcel.codeplex.com/workitem/10) -- Alternative syntax: Addressing a cell - @MarkBaker [CodePlex #11](https://phpexcel.codeplex.com/workitem/11) -- Merge cells - @MarkBaker [CodePlex #12](https://phpexcel.codeplex.com/workitem/12) - -### Bugfixes - -- Protect ranges of cells with a password - @MarkBaker [CodePlex #13](https://phpexcel.codeplex.com/workitem/13) -- (style/fill/patternFill/fgColor or bgColor can be empty) - @JV [CodePlex #14](https://phpexcel.codeplex.com/workitem/14) - -## [1.1.1] - 2007-03-26 - -### General - -- Syntax error in "Classes/PHPExcel/Writer/Excel2007.php" on line 243 - @MarkBaker [CodePlex #1250](https://phpexcel.codeplex.com/workitem/1250) -- Reader should check if file exists and throws an exception when it doesn't - @MarkBaker [CodePlex #1282](https://phpexcel.codeplex.com/workitem/1282) - -## [1.1.0] - 2007-03-22 - -### Bugfixes - -- Style information lost after passing trough Excel2007_Reader - @MarkBaker [CodePlex #836](https://phpexcel.codeplex.com/workitem/836) - -### General - -- Number of columns > AZ fails fixed in PHPExcel_Cell::columnIndexFromString - @MarkBaker [CodePlex #913](https://phpexcel.codeplex.com/workitem/913) - -### Features - -- Added a brief file with installation instructions - @MarkBaker -- Page breaks (horizontal and vertical) - @MarkBaker -- Image shadows - @MarkBaker - -## [1.0.0] - 2007-02-22 - -### Bugfixes - -- PHPExcel->removeSheetByIndex now re-orders sheets after deletion, so no array indexes are lost - @JV -- PHPExcel_Writer_Excel2007_Worksheet::_writeCols() used direct assignment to $pSheet->getColumnDimension('A')->Width instead of $pSheet->getColumnDimension('A')->setWidth() - @JV -- DocumentProperties used $this->LastModifiedBy instead of $this->_lastModifiedBy. - @JV - -### General - -- Only first = should be removed when writing formula in PHPExcel_Writer_Excel2007_Worksheet. - @JV -- Consistency of method names to camelCase - @JV -- Updated tests to match consistency changes - @JV -- Detection of mime-types now with image_type_to_mime_type() - @JV -- Constants now hold string value used in Excel 2007 - @JV - -### Features - -- Fixed folder name case (WorkSheet -> Worksheet) - @MarkBaker -- PHPExcel classes (not the Writer classes) can be duplicated, using a duplicate() method. - @MarkBaker -- Cell styles can now be duplicated to a range of cells using PHPExcel_Worksheet->duplicateStyle() - @MarkBaker -- Conditional formatting - @MarkBaker -- Reader for Excel 2007 (not supporting full specification yet!) - @JV - -## [1.0.0 RC] - 2007-01-31 - -- Project name has been changed to PHPExcel -- Project homepage is now http://www.codeplex.com/PHPExcel -- Started versioning at number: PHPExcel 1.0.0 RC - -## 2007-01-22 - -- Fixed some performance issues on large-scale worksheets (mainly loops vs. indexed arrays) -- Performance on creating StringTable has been increased -- Performance on writing Excel2007 worksheet has been increased - -## 2007-01-18 - -- Images can now be rotated -- Fixed bug: When drawings have full path specified, no mime type can be deducted -- Fixed bug: Only one drawing can be added to a worksheet - -## 2007-01-12 - -- Refactoring of some classes to use ArrayObject instead of array() -- Cell style now has support for number format (i.e. #,##0) -- Implemented embedding images - -## 2007-01-02 - -- Cell style now has support for fills, including gradient fills -- Cell style now has support for fonts -- Cell style now has support for border colors -- Cell style now has support for font colors -- Cell style now has support for alignment - -## 2006-12-21 - -- Support for cell style borders -- Support for cell styles -- Refactoring of Excel2007 Writer into multiple classes in package SpreadSheet_Writer_Excel2007 -- Refactoring of all classes, changed public members to public properties using getter/setter -- Worksheet names are now unique. On duplicate worksheet names, a number is appended. -- Worksheet now has parent SpreadSheet object -- Worksheet now has support for page header and footer -- Worksheet now has support for page margins -- Worksheet now has support for page setup (only Paper size and Orientation) -- Worksheet properties now accessible by using getProperties() -- Worksheet now has support for row and column dimensions (height / width) -- Exceptions thrown have a more clear description - -## Initial version - -- Create a Spreadsheet object -- Add one or more Worksheet objects -- Add cells to Worksheet objects -- Export Spreadsheet object to Excel 2007 OpenXML format -- Each cell supports the following data formats: string, number, formula, boolean. diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md index 1c8489d0511..a4741afe0aa 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/CHANGELOG.md @@ -5,7 +5,215 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com) and this project adheres to [Semantic Versioning](https://semver.org). -## [1.10.1] - 2019-12-02 +## Unreleased - TBD + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 1.16.0 - 2020-12-31 + +### Added + +- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647) + +### Changed + +- Updated the CONVERT() function to support all current MS Excel categories and Units of Measure. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592) +- Resolve Xlsx loader issue whe hyperlinks don't have a destination +- Resolve issues when printer settings resources IDs clash with drawing IDs +- Resolve issue with SLK long filenames [#1612](https://github.com/PHPOffice/PhpSpreadsheet/issues/1612) +- ROUNDUP and ROUNDDOWN return incorrect results for values of 0 [#1627](https://github.com/phpoffice/phpspreadsheet/pull/1627) +- Apply Column and Row Styles to Existing Cells [#1712](https://github.com/PHPOffice/PhpSpreadsheet/issues/1712) [PR #1721](https://github.com/PHPOffice/PhpSpreadsheet/pull/1721) +- Resolve issues with defined names where worksheet doesn't exist (#1686)[https://github.com/PHPOffice/PhpSpreadsheet/issues/1686] and [#1723](https://github.com/PHPOffice/PhpSpreadsheet/issues/1723) - [PR #1742](https://github.com/PHPOffice/PhpSpreadsheet/pull/1742) +- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743) +- Ensure that the list of shared formulae is maintained when an xlsx file is chunked with readFilter[Issue #169](https://github.com/PHPOffice/PhpSpreadsheet/issues/1669). +- Fix for notice during accessing "cached magnification factor" offset [#1354](https://github.com/PHPOffice/PhpSpreadsheet/pull/1354) +- Fix compatibility with ext-gd on php 8 + +### Security Fix (CVE-2020-7776) + +- Prevent XSS through cell comments in the HTML Writer. + +## 1.15.0 - 2020-10-11 + +### Added + +- Implemented Page Order for Xlsx and Xls Readers, and provided Page Settings (Orientation, Scale, Horizontal/Vertical Centering, Page Order, Margins) support for Ods, Gnumeric and Xls Readers [#1559](https://github.com/PHPOffice/PhpSpreadsheet/pull/1559) +- Implementation of the Excel `LOGNORM.DIST()`, `NORM.S.DIST()`, `GAMMA()` and `GAUSS()` functions. [#1588](https://github.com/PHPOffice/PhpSpreadsheet/pull/1588) +- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535) + - Defined Names are now case-insensitive + - Distinction between named ranges and named formulae + - Correct handling of union and intersection operators in named ranges + - Correct evaluation of named range operators in calculations + - fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute. + - Calculation support for named formulae + - Support for nested ranges and formulae (named ranges and formulae that reference other named ranges/formulae) in calculations + - Introduction of a helper to convert address formats between R1C1 and A1 (and the reverse) + - Proper support for both named ranges and named formulae in all appropriate Readers + - **Xlsx** (Previously only simple named ranges were supported) + - **Xls** (Previously only simple named ranges were supported) + - **Gnumeric** (Previously neither named ranges nor formulae were supported) + - **Ods** (Previously neither named ranges nor formulae were supported) + - **Xml** (Previously neither named ranges nor formulae were supported) + - Proper support for named ranges and named formulae in all appropriate Writers + - **Xlsx** (Previously only simple named ranges were supported) + - **Xls** (Previously neither named ranges nor formulae were supported) - Still not supported, but some parser issues resolved that previously failed to differentiate between a defined name and a function name + - **Ods** (Previously neither named ranges nor formulae were supported) +- Support for PHP 8.0 + +### Changed + +- Improve Coverage for ODS Reader [#1545](https://github.com/phpoffice/phpspreadsheet/pull/1545) +- Named formula implementation, and improved handling of Defined Names generally [#1535](https://github.com/PHPOffice/PhpSpreadsheet/pull/1535) +- fix resolution of relative named range values in the calculation engine; previously all named range values had been treated as absolute. +- Drop $this->spreadSheet null check from Xlsx Writer [#1646](https://github.com/phpoffice/phpspreadsheet/pull/1646) +- Improving Coverage for Excel2003 XML Reader [#1557](https://github.com/phpoffice/phpspreadsheet/pull/1557) + +### Deprecated + +- **IMPORTANT NOTE:** This Introduces a **BC break** in the handling of named ranges. Previously, a named range cell reference of `B2` would be treated identically to a named range cell reference of `$B2` or `B$2` or `$B$2` because the calculation engine treated then all as absolute references. These changes "fix" that, so the calculation engine now handles relative references in named ranges correctly. + This change that resolves previously incorrect behaviour in the calculation may affect users who have dynamically defined named ranges using relative references when they should have used absolute references. + +### Removed + +- Nothing. + +### Fixed + +- PrintArea causes exception [#1544](https://github.com/phpoffice/phpspreadsheet/pull/1544) +- Calculation/DateTime Failure With PHP8 [#1661](https://github.com/phpoffice/phpspreadsheet/pull/1661) +- Reader/Gnumeric Failure with PHP8 [#1662](https://github.com/phpoffice/phpspreadsheet/pull/1662) +- ReverseSort bug, exposed but not caused by PHP8 [#1660](https://github.com/phpoffice/phpspreadsheet/pull/1660) +- Bug setting Superscript/Subscript to false [#1567](https://github.com/phpoffice/phpspreadsheet/pull/1567) + +## 1.14.1 - 2020-07-19 + +### Added + +- nothing + +### Fixed + +- WEBSERVICE is HTTP client agnostic and must be configured via `Settings::setHttpClient()` [#1562](https://github.com/PHPOffice/PhpSpreadsheet/issues/1562) +- Borders were not complete on rowspanned columns using HTML reader [#1473](https://github.com/PHPOffice/PhpSpreadsheet/pull/1473) + +### Changed + +## 1.14.0 - 2020-06-29 + +### Added + +- Add support for IFS() logical function [#1442](https://github.com/PHPOffice/PhpSpreadsheet/pull/1442) +- Add Cell Address Helper to provide conversions between the R1C1 and A1 address formats [#1558](https://github.com/PHPOffice/PhpSpreadsheet/pull/1558) +- Add ability to edit Html/Pdf before saving [#1499](https://github.com/PHPOffice/PhpSpreadsheet/pull/1499) +- Add ability to set codepage explicitly for BIFF5 [#1018](https://github.com/PHPOffice/PhpSpreadsheet/issues/1018) +- Added support for the WEBSERVICE function [#1409](https://github.com/PHPOffice/PhpSpreadsheet/pull/1409) + +### Fixed + +- Resolve evaluation of utf-8 named ranges in calculation engine [#1522](https://github.com/PHPOffice/PhpSpreadsheet/pull/1522) +- Fix HLOOKUP on single row [#1512](https://github.com/PHPOffice/PhpSpreadsheet/pull/1512) +- Fix MATCH when comparing different numeric types [#1521](https://github.com/PHPOffice/PhpSpreadsheet/pull/1521) +- Fix exact MATCH on ranges with empty cells [#1520](https://github.com/PHPOffice/PhpSpreadsheet/pull/1520) +- Fix for Issue [#1516](https://github.com/PHPOffice/PhpSpreadsheet/issues/1516) (Cloning worksheet makes corrupted Xlsx) [#1530](https://github.com/PHPOffice/PhpSpreadsheet/pull/1530) +- Fix For Issue [#1509](https://github.com/PHPOffice/PhpSpreadsheet/issues/1509) (Can not set empty enclosure for CSV) [#1518](https://github.com/PHPOffice/PhpSpreadsheet/pull/1518) +- Fix for Issue [#1505](https://github.com/PHPOffice/PhpSpreadsheet/issues/1505) (TypeError : Argument 4 passed to PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeAttributeIf() must be of the type string) [#1525](https://github.com/PHPOffice/PhpSpreadsheet/pull/1525) +- Fix for Issue [#1495](https://github.com/PHPOffice/PhpSpreadsheet/issues/1495) (Sheet index being changed when multiple sheets are used in formula) [#1500]((https://github.com/PHPOffice/PhpSpreadsheet/pull/1500)) +- Fix for Issue [#1533](https://github.com/PHPOffice/PhpSpreadsheet/issues/1533) (A reference to a cell containing a string starting with "#" leads to errors in the generated xlsx.) [#1534](https://github.com/PHPOffice/PhpSpreadsheet/pull/1534) +- Xls Writer - Correct Timestamp Bug [#1493](https://github.com/PHPOffice/PhpSpreadsheet/pull/1493) +- Don't ouput row and columns without any cells in HTML writer [#1235](https://github.com/PHPOffice/PhpSpreadsheet/issues/1235) + +## 1.13.0 - 2020-05-31 + +### Added + +- Support writing to streams in all writers [#1292](https://github.com/PHPOffice/PhpSpreadsheet/issues/1292) +- Support CSV files with data wrapping a lot of lines [#1468](https://github.com/PHPOffice/PhpSpreadsheet/pull/1468) +- Support protection of worksheet by a specific hash algorithm [#1485](https://github.com/PHPOffice/PhpSpreadsheet/pull/1485) + +### Fixed + +- Fix Chart samples by updating chart parameter from 0 to DataSeries::EMPTY_AS_GAP [#1448](https://github.com/PHPOffice/PhpSpreadsheet/pull/1448) +- Fix return type in docblock for the Cells::get() [#1398](https://github.com/PHPOffice/PhpSpreadsheet/pull/1398) +- Fix RATE, PRICE, XIRR, and XNPV Functions [#1456](https://github.com/PHPOffice/PhpSpreadsheet/pull/1456) +- Save Excel 2010+ functions properly in XLSX [#1461](https://github.com/PHPOffice/PhpSpreadsheet/pull/1461) +- Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464) +- Fix incorrect behaviour when saving XLSX file with drawings [#1462](https://github.com/PHPOffice/PhpSpreadsheet/pull/1462), +- Fix Crash while trying setting a cell the value "123456\n" [#1476](https://github.com/PHPOffice/PhpSpreadsheet/pull/1481) +- Improved DATEDIF() function and reduced errors for Y and YM units [#1466](https://github.com/PHPOffice/PhpSpreadsheet/pull/1466) +- Stricter typing for mergeCells [#1494](https://github.com/PHPOffice/PhpSpreadsheet/pull/1494) + +### Changed + +- Drop support for PHP 7.1, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support +- Drop partial migration tool in favor of complete migration via RectorPHP [#1445](https://github.com/PHPOffice/PhpSpreadsheet/issues/1445) +- Limit composer package to `src/` [#1424](https://github.com/PHPOffice/PhpSpreadsheet/pull/1424) + +## 1.12.0 - 2020-04-27 + +### Added + +- Improved the ARABIC function to also handle short-hand roman numerals +- Added support for the FLOOR.MATH and FLOOR.PRECISE functions [#1351](https://github.com/PHPOffice/PhpSpreadsheet/pull/1351) + +### Fixed + +- Fix ROUNDUP and ROUNDDOWN for floating-point rounding error [#1404](https://github.com/PHPOffice/PhpSpreadsheet/pull/1404) +- Fix ROUNDUP and ROUNDDOWN for negative number [#1417](https://github.com/PHPOffice/PhpSpreadsheet/pull/1417) +- Fix loading styles from vmlDrawings when containing whitespace [#1347](https://github.com/PHPOffice/PhpSpreadsheet/issues/1347) +- Fix incorrect behavior when removing last row [#1365](https://github.com/PHPOffice/PhpSpreadsheet/pull/1365) +- MATCH with a static array should return the position of the found value based on the values submitted [#1332](https://github.com/PHPOffice/PhpSpreadsheet/pull/1332) +- Fix Xlsx Reader's handling of undefined fill color [#1353](https://github.com/PHPOffice/PhpSpreadsheet/pull/1353) + +## 1.11.0 - 2020-03-02 + +### Added + +- Added support for the BASE function +- Added support for the ARABIC function +- Conditionals - Extend Support for (NOT)CONTAINSBLANKS [#1278](https://github.com/PHPOffice/PhpSpreadsheet/pull/1278) + +### Fixed + +- Handle Error in Formula Processing Better for Xls [#1267](https://github.com/PHPOffice/PhpSpreadsheet/pull/1267) +- Handle ConditionalStyle NumberFormat When Reading Xlsx File [#1296](https://github.com/PHPOffice/PhpSpreadsheet/pull/1296) +- Fix Xlsx Writer's handling of decimal commas [#1282](https://github.com/PHPOffice/PhpSpreadsheet/pull/1282) +- Fix for issue by removing test code mistakenly left in [#1328](https://github.com/PHPOffice/PhpSpreadsheet/pull/1328) +- Fix for Xls writer wrong selected cells and active sheet [#1256](https://github.com/PHPOffice/PhpSpreadsheet/pull/1256) +- Fix active cell when freeze pane is used [#1323](https://github.com/PHPOffice/PhpSpreadsheet/pull/1323) +- Fix XLSX file loading with autofilter containing '$' [#1326](https://github.com/PHPOffice/PhpSpreadsheet/pull/1326) +- PHPDoc - Use `@return $this` for fluent methods [#1362](https://github.com/PHPOffice/PhpSpreadsheet/pull/1362) + +## 1.10.1 - 2019-12-02 ### Changed @@ -20,7 +228,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - ODS writer prevent invalid numeric value if locale decimal separator is comma [#1268](https://github.com/PHPOffice/PhpSpreadsheet/pull/1268) - Xlsx writer actually writes plotVisOnly and dispBlanksAs from chart properties [#1266](https://github.com/PHPOffice/PhpSpreadsheet/pull/1266) -## [1.10.0] - 2019-11-18 +## 1.10.0 - 2019-11-18 ### Changed @@ -45,7 +253,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Validate XIRR inputs and return correct error values [#1120](https://github.com/PHPOffice/PhpSpreadsheet/issues/1120) - Allow to read xlsx files with exotic workbook names like "workbook2.xml" [#1183](https://github.com/PHPOffice/PhpSpreadsheet/pull/1183) -## [1.9.0] - 2019-08-17 +## 1.9.0 - 2019-08-17 ### Changed @@ -76,20 +284,20 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix `getCalculatedValue()` error with more than two INDIRECT [#1115](https://github.com/PHPOffice/PhpSpreadsheet/pull/1115) - Writer\Html did not hide columns [#985](https://github.com/PHPOffice/PhpSpreadsheet/pull/985) -## [1.8.2] - 2019-07-08 +## 1.8.2 - 2019-07-08 ### Fixed - Uncaught error when opening ods file and properties aren't defined [#1047](https://github.com/PHPOffice/PhpSpreadsheet/issues/1047) - Xlsx Reader Cell datavalidations bug [#1052](https://github.com/PHPOffice/PhpSpreadsheet/pull/1052) -## [1.8.1] - 2019-07-02 +## 1.8.1 - 2019-07-02 ### Fixed - Allow nullable theme for Xlsx Style Reader class [#1043](https://github.com/PHPOffice/PhpSpreadsheet/issues/1043) -## [1.8.0] - 2019-07-01 +## 1.8.0 - 2019-07-01 ### Security Fix (CVE-2019-12331) @@ -112,7 +320,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix a SUMIF warning with some versions of PHP when having different length of arrays provided as input [#873](https://github.com/PHPOffice/PhpSpreadsheet/pull/873) - Fix incorrectly handled backslash-escaped space characters in number format -## [1.7.0] - 2019-05-26 +## 1.7.0 - 2019-05-26 - Added support for inline styles in Html reader (borders, alignment, width, height) - QuotedText cells no longer treated as formulae if the content begins with a `=` @@ -126,7 +334,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix VLOOKUP - Fix return type hint -## [1.6.0] - 2019-01-02 +## 1.6.0 - 2019-01-02 ### Added @@ -154,13 +362,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - `master` is the new default branch, `develop` does not exist anymore -## [1.5.2] - 2018-11-25 +## 1.5.2 - 2018-11-25 ### Security - Improvements to the design of the XML Security Scanner [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771) -## [1.5.1] - 2018-11-20 +## 1.5.1 - 2018-11-20 ### Security @@ -180,7 +388,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Mpdf export can exceed pcre.backtrack_limit [#637](https://github.com/PHPOffice/PhpSpreadsheet/issues/637) - Fix index overflow on data values array [#748](https://github.com/PHPOffice/PhpSpreadsheet/pull/748) -## [1.5.0] - 2018-10-21 +## 1.5.0 - 2018-10-21 ### Added @@ -195,7 +403,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - OFFSET should allow omitted height and width [#561](https://github.com/PHPOffice/PhpSpreadsheet/issues/561) - Correctly determine delimiter when CSV contains line breaks inside enclosures [#716](https://github.com/PHPOffice/PhpSpreadsheet/issues/716) -## [1.4.1] - 2018-09-30 +## 1.4.1 - 2018-09-30 ### Fixed @@ -204,7 +412,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fix warning when reading xlsx without styles [#631](https://github.com/PHPOffice/PhpSpreadsheet/pull/631) - Fix broken sample links on windows due to $baseDir having backslash [#653](https://github.com/PHPOffice/PhpSpreadsheet/pull/653) -## [1.4.0] - 2018-08-06 +## 1.4.0 - 2018-08-06 ### Added @@ -268,13 +476,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Migration tool keep variables containing $PHPExcel untouched [#598](https://github.com/PHPOffice/PhpSpreadsheet/issues/598) - Rowspans/colspans were incorrect when adding worksheet using loadIntoExisting [#619](https://github.com/PHPOffice/PhpSpreadsheet/issues/619) -## [1.3.1] - 2018-06-12 +## 1.3.1 - 2018-06-12 ### Fixed - Ranges across Z and AA columns incorrectly threw an exception [#545](https://github.com/PHPOffice/PhpSpreadsheet/issues/545) -## [1.3.0] - 2018-06-10 +## 1.3.0 - 2018-06-10 ### Added @@ -293,13 +501,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Fixed parsing of conditionals in COUNTIF functions [#526](https://github.com/PHPOffice/PhpSpreadsheet/issues/526) - Corruption errors for saved Xlsx docs with frozen panes [#532](https://github.com/PHPOffice/PhpSpreadsheet/issues/532) -## [1.2.1] - 2018-04-10 +## 1.2.1 - 2018-04-10 ### Fixed - Plain text and richtext mixed in same cell can be read [#442](https://github.com/PHPOffice/PhpSpreadsheet/issues/442) -## [1.2.0] - 2018-03-04 +## 1.2.0 - 2018-03-04 ### Added @@ -317,7 +525,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Select correct cell when calling freezePane [#389](https://github.com/PHPOffice/PhpSpreadsheet/issues/389) - `setStrikethrough()` did not set the font [#403](https://github.com/PHPOffice/PhpSpreadsheet/issues/403) -## [1.1.0] - 2018-01-28 +## 1.1.0 - 2018-01-28 ### Added @@ -335,7 +543,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Support XML file without styles [#331](https://github.com/PHPOffice/PhpSpreadsheet/pull/331) - Cell coordinates which are already a range cause an exception [#319](https://github.com/PHPOffice/PhpSpreadsheet/issues/319) -## [1.0.0] - 2017-12-25 +## 1.0.0 - 2017-12-25 ### Added @@ -354,7 +562,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Constant `TYPE_DOUGHTNUTCHART` is now `TYPE_DOUGHNUTCHART`. -## [1.0.0-beta2] - 2017-11-26 +## 1.0.0-beta2 - 2017-11-26 ### Added @@ -392,7 +600,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - `PhpSpreadsheet\Style` => `PhpSpreadsheet\Style\Style` - `PhpSpreadsheet\Worksheet` => `PhpSpreadsheet\Worksheet\Worksheet` -## [1.0.0-beta] - 2017-08-17 +## 1.0.0-beta - 2017-08-17 ### Added @@ -412,6 +620,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Ignore inlineStr type if formula element exists - @ncrypthic [#570](https://github.com/PHPOffice/PHPExcel/issues/570) - Excel 2007 Reader freezes because of conditional formatting - @rentalhost [#575](https://github.com/PHPOffice/PHPExcel/issues/575) - Readers will now parse files containing worksheet titles over 31 characters [#176](https://github.com/PHPOffice/PhpSpreadsheet/pull/176) +- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625) ### General diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/README.md b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/README.md new file mode 100644 index 00000000000..2a94e0d3d56 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/README.md @@ -0,0 +1,30 @@ +# PhpSpreadsheet + +[![Build Status](https://github.com/PHPOffice/PhpSpreadsheet/workflows/main/badge.svg)](https://github.com/PHPOffice/PhpSpreadsheet/actions) +[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master) +[![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![Latest Stable Version](https://img.shields.io/github/v/release/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![License](https://img.shields.io/github/license/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet) +[![Join the chat at https://gitter.im/PHPOffice/PhpSpreadsheet](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PhpSpreadsheet) + +PhpSpreadsheet is a library written in pure PHP and offers a set of classes that +allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc. + +## Documentation + +Read more about it, including install instructions, in the [official documentation](https://phpspreadsheet.readthedocs.io). Or check out the [API documentation](https://phpoffice.github.io/PhpSpreadsheet). + +Please ask your support questions on [StackOverflow](https://stackoverflow.com/questions/tagged/phpspreadsheet), or have a quick chat on [Gitter](https://gitter.im/PHPOffice/PhpSpreadsheet). + +## PHPExcel vs PhpSpreadsheet ? + +PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.). + +Because all efforts have shifted to PhpSpreadsheet, PHPExcel will no longer be maintained. All contributions for PHPExcel, patches and new features, should target PhpSpreadsheet `master` branch. + +Do you need to migrate? There is [an automated tool](/docs/topics/migration-from-PHPExcel.md) for that. + +## License + +PhpSpreadsheet is licensed under [MIT](https://github.com/PHPOffice/PhpSpreadsheet/blob/master/LICENSE). diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json index cfff1cb1ab3..c6f8e30e890 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.json @@ -28,18 +28,18 @@ "scripts": { "check": [ "php-cs-fixer fix --ansi --dry-run --diff", - "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n", + "phpcs", "phpunit --color=always" ], "fix": [ "php-cs-fixer fix --ansi" ], "versions": [ - "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.1- -n" + "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.2- -n" ] }, "require": { - "php": "^7.1", + "php": "^7.2||^8.0", "ext-ctype": "*", "ext-dom": "*", "ext-gd": "*", @@ -53,24 +53,28 @@ "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", - "markbaker/complex": "^1.4", - "markbaker/matrix": "^1.2", - "psr/simple-cache": "^1.0" + "maennchen/zipstream-php": "^2.1", + "markbaker/complex": "^1.5||^2.0", + "markbaker/matrix": "^1.2||^2.0", + "psr/simple-cache": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "ezyang/htmlpurifier": "^4.13" }, "require-dev": { - "dompdf/dompdf": "^0.8.3", + "dompdf/dompdf": "^0.8.5", "friendsofphp/php-cs-fixer": "^2.16", "jpgraph/jpgraph": "^4.0", "mpdf/mpdf": "^8.0", "phpcompatibility/php-compatibility": "^9.3", - "phpunit/phpunit": "^7.5", + "phpunit/phpunit": "^8.5||^9.3", "squizlabs/php_codesniffer": "^3.5", "tecnickcom/tcpdf": "^6.3" }, "suggest": { "mpdf/mpdf": "Option for rendering PDF with PDF Writer", - "dompdf/dompdf": "Option for rendering PDF with PDF Writer", - "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer", + "dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers" }, "autoload": { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.lock b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.lock deleted file mode 100644 index 9299919f5de..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/composer.lock +++ /dev/null @@ -1,3503 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "0fd32acfbb0d21f168f495840ffc8d7e", - "packages": [ - { - "name": "markbaker/complex", - "version": "1.4.7", - "source": { - "type": "git", - "url": "https://github.com/MarkBaker/PHPComplex.git", - "reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/1ea674a8308baf547cbcbd30c5fcd6d301b7c000", - "reference": "1ea674a8308baf547cbcbd30c5fcd6d301b7c000", - "shasum": "" - }, - "require": { - "php": "^5.6.0|^7.0.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3", - "phpcompatibility/php-compatibility": "^8.0", - "phpdocumentor/phpdocumentor": "2.*", - "phploc/phploc": "2.*", - "phpmd/phpmd": "2.*", - "phpunit/phpunit": "^4.8.35|^5.4.0", - "sebastian/phpcpd": "2.*", - "squizlabs/php_codesniffer": "^3.3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Complex\\": "classes/src/" - }, - "files": [ - "classes/src/functions/abs.php", - "classes/src/functions/acos.php", - "classes/src/functions/acosh.php", - "classes/src/functions/acot.php", - "classes/src/functions/acoth.php", - "classes/src/functions/acsc.php", - "classes/src/functions/acsch.php", - "classes/src/functions/argument.php", - "classes/src/functions/asec.php", - "classes/src/functions/asech.php", - "classes/src/functions/asin.php", - "classes/src/functions/asinh.php", - "classes/src/functions/atan.php", - "classes/src/functions/atanh.php", - "classes/src/functions/conjugate.php", - "classes/src/functions/cos.php", - "classes/src/functions/cosh.php", - "classes/src/functions/cot.php", - "classes/src/functions/coth.php", - "classes/src/functions/csc.php", - "classes/src/functions/csch.php", - "classes/src/functions/exp.php", - "classes/src/functions/inverse.php", - "classes/src/functions/ln.php", - "classes/src/functions/log2.php", - "classes/src/functions/log10.php", - "classes/src/functions/negative.php", - "classes/src/functions/pow.php", - "classes/src/functions/rho.php", - "classes/src/functions/sec.php", - "classes/src/functions/sech.php", - "classes/src/functions/sin.php", - "classes/src/functions/sinh.php", - "classes/src/functions/sqrt.php", - "classes/src/functions/tan.php", - "classes/src/functions/tanh.php", - "classes/src/functions/theta.php", - "classes/src/operations/add.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" - } - ], - "description": "PHP Class for working with complex numbers", - "homepage": "https://github.com/MarkBaker/PHPComplex", - "keywords": [ - "complex", - "mathematics" - ], - "time": "2018-10-13T23:28:42+00:00" - }, - { - "name": "markbaker/matrix", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/MarkBaker/PHPMatrix.git", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/5348c5a67e3b75cd209d70103f916a93b1f1ed21", - "reference": "5348c5a67e3b75cd209d70103f916a93b1f1ed21", - "shasum": "" - }, - "require": { - "php": "^5.6.0|^7.0.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "dev-master", - "phpcompatibility/php-compatibility": "dev-master", - "phploc/phploc": "^4", - "phpmd/phpmd": "dev-master", - "phpunit/phpunit": "^5.7", - "sebastian/phpcpd": "^3.0", - "squizlabs/php_codesniffer": "^3.0@dev" - }, - "type": "library", - "autoload": { - "psr-4": { - "Matrix\\": "classes/src/" - }, - "files": [ - "classes/src/functions/adjoint.php", - "classes/src/functions/antidiagonal.php", - "classes/src/functions/cofactors.php", - "classes/src/functions/determinant.php", - "classes/src/functions/diagonal.php", - "classes/src/functions/identity.php", - "classes/src/functions/inverse.php", - "classes/src/functions/minors.php", - "classes/src/functions/trace.php", - "classes/src/functions/transpose.php", - "classes/src/operations/add.php", - "classes/src/operations/directsum.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mark Baker", - "email": "mark@lange.demon.co.uk" - } - ], - "description": "PHP Class for working with matrices", - "homepage": "https://github.com/MarkBaker/PHPMatrix", - "keywords": [ - "mathematics", - "matrix", - "vector" - ], - "time": "2019-10-06T11:29:25+00:00" - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], - "time": "2017-10-23T01:57:42+00:00" - } - ], - "packages-dev": [ - { - "name": "composer/semver", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", - "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "time": "2019-03-19T17:25:45+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "cbe23383749496fe0f373345208b79568e4bc248" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", - "reference": "cbe23383749496fe0f373345208b79568e4bc248", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "time": "2019-11-06T16:40:04+00:00" - }, - { - "name": "doctrine/annotations", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^7.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2019-10-01T18:55:10+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2019-03-17T17:37:11+00:00" - }, - { - "name": "doctrine/lexer", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "^4.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "time": "2019-06-08T11:03:04+00:00" - }, - { - "name": "dompdf/dompdf", - "version": "v0.8.3", - "source": { - "type": "git", - "url": "https://github.com/dompdf/dompdf.git", - "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/75f13c700009be21a1965dc2c5b68a8708c22ba2", - "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "phenx/php-font-lib": "0.5.*", - "phenx/php-svg-lib": "0.3.*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.5|^6.5", - "squizlabs/php_codesniffer": "2.*" - }, - "suggest": { - "ext-gd": "Needed to process images", - "ext-gmagick": "Improves image processing performance", - "ext-imagick": "Improves image processing performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "0.7-dev" - } - }, - "autoload": { - "psr-4": { - "Dompdf\\": "src/" - }, - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1" - ], - "authors": [ - { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" - }, - { - "name": "Brian Sweeney", - "email": "eclecticgeek@gmail.com" - }, - { - "name": "Gabriel Bull", - "email": "me@gabrielbull.com" - } - ], - "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", - "homepage": "https://github.com/dompdf/dompdf", - "time": "2018-12-14T02:40:31+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v2.16.1", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c8afb599858876e95e8ebfcd97812d383fa23f02", - "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02", - "shasum": "" - }, - "require": { - "composer/semver": "^1.4", - "composer/xdebug-handler": "^1.2", - "doctrine/annotations": "^1.2", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^5.6 || ^7.0", - "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", - "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^3.0 || ^4.0 || ^5.0", - "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", - "symfony/polyfill-php70": "^1.0", - "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0 || ^5.0", - "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" - }, - "require-dev": { - "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", - "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.2", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.1", - "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", - "phpunitgoodpractices/traits": "^1.8", - "symfony/phpunit-bridge": "^4.3 || ^5.0", - "symfony/yaml": "^3.0 || ^4.0 || ^5.0" - }, - "suggest": { - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", - "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - }, - "classmap": [ - "tests/Test/AbstractFixerTestCase.php", - "tests/Test/AbstractIntegrationCaseFactory.php", - "tests/Test/AbstractIntegrationTestCase.php", - "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php", - "tests/Test/IntegrationCaseFactoryInterface.php", - "tests/Test/InternalIntegrationCaseFactory.php", - "tests/TestCase.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "time": "2019-11-25T22:10:32+00:00" - }, - { - "name": "jpgraph/jpgraph", - "version": "4.0.2", - "source": { - "type": "git", - "url": "https://github.com/ztec/JpGraph.git", - "reference": "e82db7da6a546d3926c24c9a346226da7aa49094" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ztec/JpGraph/zipball/e82db7da6a546d3926c24c9a346226da7aa49094", - "reference": "e82db7da6a546d3926c24c9a346226da7aa49094", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "lib/JpGraph.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "QPL 1.0" - ], - "authors": [ - { - "name": "JpGraph team" - } - ], - "description": "jpGraph, library to make graphs and charts", - "homepage": "http://jpgraph.net/", - "keywords": [ - "chart", - "data", - "graph", - "jpgraph", - "pie" - ], - "time": "2017-02-23T09:44:15+00:00" - }, - { - "name": "mpdf/mpdf", - "version": "v8.0.4", - "source": { - "type": "git", - "url": "https://github.com/mpdf/mpdf.git", - "reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mpdf/mpdf/zipball/d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f", - "reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "ext-mbstring": "*", - "myclabs/deep-copy": "^1.7", - "paragonie/random_compat": "^1.4|^2.0|9.99.99", - "php": "^5.6 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0", - "psr/log": "^1.0", - "setasign/fpdi": "^2.1" - }, - "require-dev": { - "mockery/mockery": "^0.9.5", - "mpdf/qrcode": "^1.0.0", - "phpunit/phpunit": "^5.0", - "squizlabs/php_codesniffer": "^3.5.0", - "tracy/tracy": "^2.4" - }, - "suggest": { - "ext-bcmath": "Needed for generation of some types of barcodes", - "ext-xml": "Needed mainly for SVG manipulation", - "ext-zlib": "Needed for compression of embedded resources, such as fonts" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-development": "7.x-dev" - } - }, - "autoload": { - "psr-4": { - "Mpdf\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0-only" - ], - "authors": [ - { - "name": "Matěj Humpál", - "role": "Developer, maintainer" - }, - { - "name": "Ian Back", - "role": "Developer (retired)" - } - ], - "description": "PHP library generating PDF files from UTF-8 encoded HTML", - "homepage": "https://mpdf.github.io", - "keywords": [ - "pdf", - "php", - "utf-8" - ], - "time": "2019-11-28T09:39:33+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.9.3", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2019-08-09T12:45:53+00:00" - }, - { - "name": "paragonie/random_compat", - "version": "v9.99.99", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "shasum": "" - }, - "require": { - "php": "^7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "time": "2018-07-02T15:55:56+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phenx/php-font-lib", - "version": "0.5.1", - "source": { - "type": "git", - "url": "https://github.com/PhenX/php-font-lib.git", - "reference": "760148820110a1ae0936e5cc35851e25a938bc97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97", - "reference": "760148820110a1ae0936e5cc35851e25a938bc97", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "^4.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "FontLib\\": "src/FontLib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" - } - ], - "description": "A library to read, parse, export and make subsets of different types of font files.", - "homepage": "https://github.com/PhenX/php-font-lib", - "time": "2017-09-13T16:14:37+00:00" - }, - { - "name": "phenx/php-svg-lib", - "version": "v0.3.3", - "source": { - "type": "git", - "url": "https://github.com/PhenX/php-svg-lib.git", - "reference": "5fa61b65e612ce1ae15f69b3d223cb14ecc60e32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/5fa61b65e612ce1ae15f69b3d223cb14ecc60e32", - "reference": "5fa61b65e612ce1ae15f69b3d223cb14ecc60e32", - "shasum": "" - }, - "require": { - "sabberworm/php-css-parser": "^8.3" - }, - "require-dev": { - "phpunit/phpunit": "^5.5|^6.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Svg\\": "src/Svg" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" - } - ], - "description": "A library to read, parse and export to PDF SVG files.", - "homepage": "https://github.com/PhenX/php-svg-lib", - "time": "2019-09-11T20:02:13+00:00" - }, - { - "name": "php-cs-fixer/diff", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "symfony/process": "^3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "SpacePossum" - } - ], - "description": "sebastian/diff v2 backport support for PHP5.6", - "homepage": "https://github.com/PHP-CS-Fixer", - "keywords": [ - "diff" - ], - "time": "2018-02-15T16:58:55+00:00" - }, - { - "name": "phpcompatibility/php-compatibility", - "version": "9.3.2", - "source": { - "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "bfca2be3992f40e92206e5a7ebe5eaee37280b58" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/bfca2be3992f40e92206e5a7ebe5eaee37280b58", - "reference": "bfca2be3992f40e92206e5a7ebe5eaee37280b58", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0-or-later" - ], - "authors": [ - { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" - } - ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], - "time": "2019-10-16T21:24:24+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2018-08-07T13:53:10+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", - "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "^1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-09-12T14:27:41+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "shasum": "" - }, - "require": { - "php": "^7.1", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.9.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2019-10-03T11:07:50+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "6.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-xdebug": "^2.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-10-31T16:06:48+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2018-09-13T20:33:42+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2019-06-07T04:22:29+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2019-09-17T06:23:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "7.5.17", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4c92a15296e58191a4cd74cff3b34fc8e374174a", - "reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2019-10-28T10:37:36+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/log", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2019-11-01T11:05:21+00:00" - }, - { - "name": "sabberworm/php-css-parser", - "version": "8.3.0", - "source": { - "type": "git", - "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", - "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f", - "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "codacy/coverage": "^1.4", - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "autoload": { - "psr-0": { - "Sabberworm\\CSS": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Raphael Schweikert" - } - ], - "description": "Parser for CSS Files written in PHP", - "homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser", - "keywords": [ - "css", - "parser", - "stylesheet" - ], - "time": "2019-02-22T07:42:52+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "shasum": "" - }, - "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-07-12T15:12:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "time": "2019-02-04T06:01:07+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2019-05-05T09:05:15+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2019-09-14T09:02:43+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "setasign/fpdi", - "version": "v2.2.0", - "source": { - "type": "git", - "url": "https://github.com/Setasign/FPDI.git", - "reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/3c266002f8044f61b17329f7cd702d44d73f0f7f", - "reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f", - "shasum": "" - }, - "require": { - "ext-zlib": "*", - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "setasign/fpdf": "~1.8", - "setasign/tfpdf": "1.25", - "tecnickcom/tcpdf": "~6.2" - }, - "suggest": { - "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured.", - "setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.", - "setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF.", - "setasign/fpdi-tfpdf": "Use this package to automatically evaluate dependencies to tFPDF." - }, - "type": "library", - "autoload": { - "psr-4": { - "setasign\\Fpdi\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Slabon", - "email": "jan.slabon@setasign.com", - "homepage": "https://www.setasign.com" - }, - { - "name": "Maximilian Kresse", - "email": "maximilian.kresse@setasign.com", - "homepage": "https://www.setasign.com" - } - ], - "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", - "homepage": "https://www.setasign.com/fpdi", - "keywords": [ - "fpdf", - "fpdi", - "pdf" - ], - "time": "2019-01-30T14:11:19+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.5.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/65b12cdeaaa6cd276d4c3033a95b9b88b12701e7", - "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2019-10-28T04:36:32+00:00" - }, - { - "name": "symfony/console", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "136c4bd62ea871d00843d1bc0316de4c4a84bb78" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/136c4bd62ea871d00843d1bc0316de4c4a84bb78", - "reference": "136c4bd62ea871d00843d1bc0316de4c4a84bb78", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0", - "symfony/var-dumper": "^4.3" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2019-10-30T12:58:49+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6229f58993e5a157f6096fc7145c0717d0be8807", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/event-dispatcher-contracts": "^1.1" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "1.1" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/http-foundation": "^3.4|^4.0", - "symfony/service-contracts": "^1.1", - "symfony/stopwatch": "~3.4|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2019-10-01T16:40:32+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "suggest": { - "psr/event-dispatcher": "", - "symfony/event-dispatcher-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-09-17T09:54:03+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" - }, - { - "name": "symfony/finder", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2019-10-30T12:53:54+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony OptionsResolver Component", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "time": "2019-10-28T20:59:01+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/polyfill-php70", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "54b4c428a0054e254223797d2713c31e08610831" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", - "reference": "54b4c428a0054e254223797d2713c31e08610831", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/polyfill-php72", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "04ce3335667451138df4307d6a9b61565560199e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", - "reference": "04ce3335667451138df4307d6a9b61565560199e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188", - "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/process", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v1.1.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/container": "^1.0" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-10-14T12:27:06+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v4.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/1e4ff456bd625be5032fac9be4294e60442e9b71", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/service-contracts": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Stopwatch Component", - "homepage": "https://symfony.com", - "time": "2019-08-07T11:52:19+00:00" - }, - { - "name": "tecnickcom/tcpdf", - "version": "6.3.2", - "source": { - "type": "git", - "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "9fde7bb9b404b945e7ea88fb7eccd23d9a4e324b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/9fde7bb9b404b945e7ea88fb7eccd23d9a4e324b", - "reference": "9fde7bb9b404b945e7ea88fb7eccd23d9a4e324b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "config", - "include", - "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", - "tcpdf_barcodes_1d.php", - "tcpdf_barcodes_2d.php", - "include/tcpdf_colors.php", - "include/tcpdf_filters.php", - "include/tcpdf_font_data.php", - "include/tcpdf_fonts.php", - "include/tcpdf_images.php", - "include/tcpdf_static.php", - "include/barcodes/datamatrix.php", - "include/barcodes/pdf417.php", - "include/barcodes/qrcode.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "role": "lead" - } - ], - "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", - "homepage": "http://www.tcpdf.org/", - "keywords": [ - "PDFD32000-2008", - "TCPDF", - "barcodes", - "datamatrix", - "pdf", - "pdf417", - "qrcode" - ], - "time": "2019-09-20T09:35:01+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2019-08-24T08:43:50+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^7.1", - "ext-ctype": "*", - "ext-dom": "*", - "ext-gd": "*", - "ext-iconv": "*", - "ext-fileinfo": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-xml": "*", - "ext-xmlreader": "*", - "ext-xmlwriter": "*", - "ext-zip": "*", - "ext-zlib": "*" - }, - "platform-dev": [] -} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/mkdocs.yml b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/mkdocs.yml deleted file mode 100644 index cf87a142a33..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ -site_name: PhpSpreadsheet Documentation -repo_url: https://github.com/PHPOffice/phpspreadsheet -edit_uri: edit/master/docs/ - -theme: readthedocs -extra_css: - - extra/extra.css diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/phpunit.xml.dist b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/phpunit.xml.dist deleted file mode 100644 index be3643d8a41..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/phpunit.xml.dist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - ./tests/PhpSpreadsheetTests - - - - ./src - - ./src/PhpSpreadsheet/Shared/JAMA - ./src/PhpSpreadsheet/Writer/PDF - - - - diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/Bootstrap.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/Bootstrap.php deleted file mode 100644 index f15de26a6c4..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/Bootstrap.php +++ /dev/null @@ -1,22 +0,0 @@ -=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])'; - // Named Range of cells - const CALCULATION_REGEXP_NAMEDRANGE = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)'; + // Cell reference (with or without a sheet reference) ensuring absolute/relative + const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])'; + // Cell ranges ensuring absolute/relative + const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})'; + const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})'; + // Defined Names: Named Range of cells, or Named Formulae + const CALCULATION_REGEXP_DEFINEDNAME = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_\p{L}][_\p{L}\p{N}\.]*)'; // Error const CALCULATION_REGEXP_ERROR = '\#[A-Z][A-Z0_\/]*[!\?]?'; @@ -36,6 +43,10 @@ class Calculation const RETURN_ARRAY_AS_VALUE = 'value'; const RETURN_ARRAY_AS_ARRAY = 'array'; + const FORMULA_OPEN_FUNCTION_BRACE = '{'; + const FORMULA_CLOSE_FUNCTION_BRACE = '}'; + const FORMULA_STRING_QUOTE = '"'; + private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE; /** @@ -123,6 +134,13 @@ class Calculation */ public $formulaError; + /** + * Reference Helper. + * + * @var ReferenceHelper + */ + private static $referenceHelper; + /** * An array of the nested cell references accessed by the calculation engine, used for the debug log. * @@ -248,6 +266,11 @@ class Calculation 'functionCall' => [LookupRef::class, 'cellAddress'], 'argumentCount' => '2-5', ], + 'AGGREGATE' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3+', + ], 'AMORDEGRC' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'AMORDEGRC'], @@ -263,6 +286,11 @@ class Calculation 'functionCall' => [Logical::class, 'logicalAnd'], 'argumentCount' => '1+', ], + 'ARABIC' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [MathTrig::class, 'ARABIC'], + 'argumentCount' => '1', + ], 'AREAS' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => [Functions::class, 'DUMMY'], @@ -328,6 +356,11 @@ class Calculation 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '1', ], + 'BASE' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [MathTrig::class, 'BASE'], + 'argumentCount' => '2,3', + ], 'BESSELI' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'BESSELI'], @@ -353,11 +386,21 @@ class Calculation 'functionCall' => [Statistical::class, 'BETADIST'], 'argumentCount' => '3-5', ], + 'BETA.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '4-6', + ], 'BETAINV' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'BETAINV'], 'argumentCount' => '3-5', ], + 'BETA.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'BETAINV'], + 'argumentCount' => '3-5', + ], 'BIN2DEC' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'BINTODEC'], @@ -378,6 +421,21 @@ class Calculation 'functionCall' => [Statistical::class, 'BINOMDIST'], 'argumentCount' => '4', ], + 'BINOM.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'BINOMDIST'], + 'argumentCount' => '4', + ], + 'BINOM.DIST.RANGE' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3,4', + ], + 'BINOM.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], 'BITAND' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'BITAND'], @@ -408,6 +466,16 @@ class Calculation 'functionCall' => [MathTrig::class, 'CEILING'], 'argumentCount' => '2', ], + 'CEILING.MATH' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], + 'CEILING.PRECISE' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'CELL' => [ 'category' => Category::CATEGORY_INFORMATION, 'functionCall' => [Functions::class, 'DUMMY'], @@ -423,16 +491,41 @@ class Calculation 'functionCall' => [Statistical::class, 'CHIDIST'], 'argumentCount' => '2', ], + 'CHISQ.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], + 'CHISQ.DIST.RT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'CHIDIST'], + 'argumentCount' => '2', + ], 'CHIINV' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'CHIINV'], 'argumentCount' => '2', ], + 'CHISQ.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], + 'CHISQ.INV.RT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'CHIINV'], + 'argumentCount' => '2', + ], 'CHITEST' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '2', ], + 'CHISQ.TEST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'CHOOSE' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => [LookupRef::class, 'CHOOSE'], @@ -464,6 +557,11 @@ class Calculation 'functionCall' => [MathTrig::class, 'COMBIN'], 'argumentCount' => '2', ], + 'COMBINA' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'COMPLEX' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'COMPLEX'], @@ -484,6 +582,16 @@ class Calculation 'functionCall' => [Statistical::class, 'CONFIDENCE'], 'argumentCount' => '3', ], + 'CONFIDENCE.NORM' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'CONFIDENCE'], + 'argumentCount' => '3', + ], + 'CONFIDENCE.T' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], 'CONVERT' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'CONVERTUOM'], @@ -574,6 +682,16 @@ class Calculation 'functionCall' => [Statistical::class, 'COVAR'], 'argumentCount' => '2', ], + 'COVARIANCE.P' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'COVAR'], + 'argumentCount' => '2', + ], + 'COVARIANCE.S' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'CRITBINOM' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'CRITBINOM'], @@ -674,6 +792,11 @@ class Calculation 'functionCall' => [Financial::class, 'DB'], 'argumentCount' => '4,5', ], + 'DBCS' => [ + 'category' => Category::CATEGORY_TEXT_AND_DATA, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1', + ], 'DCOUNT' => [ 'category' => Category::CATEGORY_DATABASE, 'functionCall' => [Database::class, 'DCOUNT'], @@ -704,6 +827,11 @@ class Calculation 'functionCall' => [Engineering::class, 'DECTOOCT'], 'argumentCount' => '1,2', ], + 'DECIMAL' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'DEGREES' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => 'rad2deg', @@ -799,6 +927,11 @@ class Calculation 'functionCall' => [Financial::class, 'EFFECT'], 'argumentCount' => '2', ], + 'ENCODEURL' => [ + 'category' => Category::CATEGORY_WEB, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1', + ], 'EOMONTH' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, 'functionCall' => [DateTime::class, 'EOMONTH'], @@ -849,6 +982,11 @@ class Calculation 'functionCall' => [Statistical::class, 'EXPONDIST'], 'argumentCount' => '3', ], + 'EXPON.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'EXPONDIST'], + 'argumentCount' => '3', + ], 'FACT' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'FACT'], @@ -869,6 +1007,26 @@ class Calculation 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '3', ], + 'F.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'FDIST2'], + 'argumentCount' => '4', + ], + 'F.DIST.RT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], + 'FILTER' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3+', + ], + 'FILTERXML' => [ + 'category' => Category::CATEGORY_WEB, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'FIND' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, 'functionCall' => [TextData::class, 'SEARCHSENSITIVE'], @@ -884,6 +1042,16 @@ class Calculation 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '3', ], + 'F.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], + 'F.INV.RT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], 'FISHER' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'FISHER'], @@ -904,11 +1072,46 @@ class Calculation 'functionCall' => [MathTrig::class, 'FLOOR'], 'argumentCount' => '2', ], + 'FLOOR.MATH' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [MathTrig::class, 'FLOORMATH'], + 'argumentCount' => '3', + ], + 'FLOOR.PRECISE' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [MathTrig::class, 'FLOORPRECISE'], + 'argumentCount' => '2', + ], 'FORECAST' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'FORECAST'], 'argumentCount' => '3', ], + 'FORECAST.ETS' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3-6', + ], + 'FORECAST.ETS.CONFINT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3-6', + ], + 'FORECAST.ETS.SEASONALITY' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2-4', + ], + 'FORECAST.ETS.STAT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3-6', + ], + 'FORECAST.LINEAR' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'FORECAST'], + 'argumentCount' => '3', + ], 'FORMULATEXT' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => [LookupRef::class, 'FORMULATEXT'], @@ -926,6 +1129,11 @@ class Calculation 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '2', ], + 'F.TEST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'FV' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'FV'], @@ -936,21 +1144,46 @@ class Calculation 'functionCall' => [Financial::class, 'FVSCHEDULE'], 'argumentCount' => '2', ], + 'GAMMA' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'GAMMAFunction'], + 'argumentCount' => '1', + ], 'GAMMADIST' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'GAMMADIST'], 'argumentCount' => '4', ], + 'GAMMA.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'GAMMADIST'], + 'argumentCount' => '4', + ], 'GAMMAINV' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'GAMMAINV'], 'argumentCount' => '3', ], + 'GAMMA.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'GAMMAINV'], + 'argumentCount' => '3', + ], 'GAMMALN' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'GAMMALN'], 'argumentCount' => '1', ], + 'GAMMALN.PRECISE' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'GAMMALN'], + 'argumentCount' => '1', + ], + 'GAUSS' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'GAUSS'], + 'argumentCount' => '1', + ], 'GCD' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'GCD'], @@ -1017,6 +1250,11 @@ class Calculation 'functionCall' => [Statistical::class, 'HYPGEOMDIST'], 'argumentCount' => '4', ], + 'HYPGEOM.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '5', + ], 'IF' => [ 'category' => Category::CATEGORY_LOGICAL, 'functionCall' => [Logical::class, 'statementIf'], @@ -1032,6 +1270,11 @@ class Calculation 'functionCall' => [Logical::class, 'IFNA'], 'argumentCount' => '2', ], + 'IFS' => [ + 'category' => Category::CATEGORY_LOGICAL, + 'functionCall' => [Logical::class, 'IFS'], + 'argumentCount' => '2+', + ], 'IMABS' => [ 'category' => Category::CATEGORY_ENGINEERING, 'functionCall' => [Engineering::class, 'IMABS'], @@ -1245,6 +1488,11 @@ class Calculation 'functionCall' => [Functions::class, 'isNumber'], 'argumentCount' => '1', ], + 'ISO.CEILING' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1,2', + ], 'ISODD' => [ 'category' => Category::CATEGORY_INFORMATION, 'functionCall' => [Functions::class, 'isOdd'], @@ -1345,6 +1593,16 @@ class Calculation 'functionCall' => [Statistical::class, 'LOGNORMDIST'], 'argumentCount' => '3', ], + 'LOGNORM.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'LOGNORMDIST2'], + 'argumentCount' => '4', + ], + 'LOGNORM.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'LOGINV'], + 'argumentCount' => '3', + ], 'LOOKUP' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, 'functionCall' => [LookupRef::class, 'LOOKUP'], @@ -1450,6 +1708,11 @@ class Calculation 'functionCall' => [Statistical::class, 'MODE'], 'argumentCount' => '1+', ], + 'MODE.MULT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1+', + ], 'MODE.SNGL' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'MODE'], @@ -1470,6 +1733,11 @@ class Calculation 'functionCall' => [MathTrig::class, 'MULTINOMIAL'], 'argumentCount' => '1+', ], + 'MUNIT' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1', + ], 'N' => [ 'category' => Category::CATEGORY_INFORMATION, 'functionCall' => [Functions::class, 'n'], @@ -1485,10 +1753,20 @@ class Calculation 'functionCall' => [Statistical::class, 'NEGBINOMDIST'], 'argumentCount' => '3', ], + 'NEGBINOM.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '4', + ], 'NETWORKDAYS' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, 'functionCall' => [DateTime::class, 'NETWORKDAYS'], - 'argumentCount' => '2+', + 'argumentCount' => '2-3', + ], + 'NETWORKDAYS.INTL' => [ + 'category' => Category::CATEGORY_DATE_AND_TIME, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2-4', ], 'NOMINAL' => [ 'category' => Category::CATEGORY_FINANCIAL, @@ -1500,21 +1778,41 @@ class Calculation 'functionCall' => [Statistical::class, 'NORMDIST'], 'argumentCount' => '4', ], + 'NORM.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'NORMDIST'], + 'argumentCount' => '4', + ], 'NORMINV' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'NORMINV'], 'argumentCount' => '3', ], + 'NORM.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'NORMINV'], + 'argumentCount' => '3', + ], 'NORMSDIST' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'NORMSDIST'], 'argumentCount' => '1', ], + 'NORM.S.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'NORMSDIST2'], + 'argumentCount' => '1,2', + ], 'NORMSINV' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'NORMSINV'], 'argumentCount' => '1', ], + 'NORM.S.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'NORMSINV'], + 'argumentCount' => '1', + ], 'NOT' => [ 'category' => Category::CATEGORY_LOGICAL, 'functionCall' => [Logical::class, 'NOT'], @@ -1607,21 +1905,51 @@ class Calculation 'functionCall' => [Statistical::class, 'PERCENTILE'], 'argumentCount' => '2', ], + 'PERCENTILE.EXC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], + 'PERCENTILE.INC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'PERCENTILE'], + 'argumentCount' => '2', + ], 'PERCENTRANK' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'PERCENTRANK'], 'argumentCount' => '2,3', ], + 'PERCENTRANK.EXC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2,3', + ], + 'PERCENTRANK.INC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'PERCENTRANK'], + 'argumentCount' => '2,3', + ], 'PERMUT' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical::class, 'PERMUT'], 'argumentCount' => '2', ], + 'PERMUTATIONA' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'PHONETIC' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '1', ], + 'PHI' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1', + ], 'PI' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => 'pi', @@ -1637,6 +1965,11 @@ class Calculation 'functionCall' => [Statistical::class, 'POISSON'], 'argumentCount' => '3', ], + 'POISSON.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'POISSON'], + 'argumentCount' => '3', + ], 'POWER' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'POWER'], @@ -1687,6 +2020,16 @@ class Calculation 'functionCall' => [Statistical::class, 'QUARTILE'], 'argumentCount' => '2', ], + 'QUARTILE.EXC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], + 'QUARTILE.INC' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'QUARTILE'], + 'argumentCount' => '2', + ], 'QUOTIENT' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'QUOTIENT'], @@ -1702,6 +2045,11 @@ class Calculation 'functionCall' => [MathTrig::class, 'RAND'], 'argumentCount' => '0', ], + 'RANDARRAY' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '0-5', + ], 'RANDBETWEEN' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'RAND'], @@ -1712,6 +2060,16 @@ class Calculation 'functionCall' => [Statistical::class, 'RANK'], 'argumentCount' => '2,3', ], + 'RANK.AVG' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2,3', + ], + 'RANK.EQ' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'RANK'], + 'argumentCount' => '2,3', + ], 'RATE' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'RATE'], @@ -1818,11 +2176,26 @@ class Calculation 'functionCall' => [DateTime::class, 'SECOND'], 'argumentCount' => '1', ], + 'SEQUENCE' => [ + 'category' => Category::CATEGORY_MATH_AND_TRIG, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'SERIESSUM' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'SERIESSUM'], 'argumentCount' => '4', ], + 'SHEET' => [ + 'category' => Category::CATEGORY_INFORMATION, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '0,1', + ], + 'SHEETS' => [ + 'category' => Category::CATEGORY_INFORMATION, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '0,1', + ], 'SIGN' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => [MathTrig::class, 'SIGN'], @@ -1843,6 +2216,11 @@ class Calculation 'functionCall' => [Statistical::class, 'SKEW'], 'argumentCount' => '1+', ], + 'SKEW.P' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1+', + ], 'SLN' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'SLN'], @@ -1858,6 +2236,16 @@ class Calculation 'functionCall' => [Statistical::class, 'SMALL'], 'argumentCount' => '2', ], + 'SORT' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1+', + ], + 'SORTBY' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2+', + ], 'SQRT' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, 'functionCall' => 'sqrt', @@ -2004,6 +2392,21 @@ class Calculation 'functionCall' => [Statistical::class, 'TDIST'], 'argumentCount' => '3', ], + 'T.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3', + ], + 'T.DIST.2T' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], + 'T.DIST.RT' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'TEXT' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, 'functionCall' => [TextData::class, 'TEXTFORMAT'], @@ -2029,6 +2432,16 @@ class Calculation 'functionCall' => [Statistical::class, 'TINV'], 'argumentCount' => '2', ], + 'T.INV' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'TINV'], + 'argumentCount' => '2', + ], + 'T.INV.2T' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2', + ], 'TODAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, 'functionCall' => [DateTime::class, 'DATENOW'], @@ -2069,6 +2482,11 @@ class Calculation 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '4', ], + 'T.TEST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '4', + ], 'TYPE' => [ 'category' => Category::CATEGORY_INFORMATION, 'functionCall' => [Functions::class, 'TYPE'], @@ -2084,6 +2502,11 @@ class Calculation 'functionCall' => [TextData::class, 'ASCIICODE'], 'argumentCount' => '1', ], + 'UNIQUE' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '1+', + ], 'UPPER' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, 'functionCall' => [TextData::class, 'UPPERCASE'], @@ -2139,6 +2562,11 @@ class Calculation 'functionCall' => [LookupRef::class, 'VLOOKUP'], 'argumentCount' => '3,4', ], + 'WEBSERVICE' => [ + 'category' => Category::CATEGORY_WEB, + 'functionCall' => [Web::class, 'WEBSERVICE'], + 'argumentCount' => '1', + ], 'WEEKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, 'functionCall' => [DateTime::class, 'WEEKDAY'], @@ -2154,21 +2582,41 @@ class Calculation 'functionCall' => [Statistical::class, 'WEIBULL'], 'argumentCount' => '4', ], + 'WEIBULL.DIST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'WEIBULL'], + 'argumentCount' => '4', + ], 'WORKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, 'functionCall' => [DateTime::class, 'WORKDAY'], - 'argumentCount' => '2+', + 'argumentCount' => '2-3', + ], + 'WORKDAY.INTL' => [ + 'category' => Category::CATEGORY_DATE_AND_TIME, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2-4', ], 'XIRR' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'XIRR'], 'argumentCount' => '2,3', ], + 'XLOOKUP' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3-6', + ], 'XNPV' => [ 'category' => Category::CATEGORY_FINANCIAL, 'functionCall' => [Financial::class, 'XNPV'], 'argumentCount' => '3', ], + 'XMATCH' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '2,3', + ], 'XOR' => [ 'category' => Category::CATEGORY_LOGICAL, 'functionCall' => [Logical::class, 'logicalXor'], @@ -2204,26 +2652,36 @@ class Calculation 'functionCall' => [Statistical::class, 'ZTEST'], 'argumentCount' => '2-3', ], + 'Z.TEST' => [ + 'category' => Category::CATEGORY_STATISTICAL, + 'functionCall' => [Statistical::class, 'ZTEST'], + 'argumentCount' => '2-3', + ], ]; // Internal functions used for special control purposes private static $controlFunctions = [ 'MKMATRIX' => [ 'argumentCount' => '*', - 'functionCall' => 'self::mkMatrix', + 'functionCall' => [__CLASS__, 'mkMatrix'], + ], + 'NAME.ERROR' => [ + 'argumentCount' => '*', + 'functionCall' => [Functions::class, 'NAME'], ], ]; - public function __construct(Spreadsheet $spreadsheet = null) + public function __construct(?Spreadsheet $spreadsheet = null) { - $this->delta = 1 * pow(10, 0 - ini_get('precision')); + $this->delta = 1 * 10 ** (0 - ini_get('precision')); $this->spreadsheet = $spreadsheet; $this->cyclicReferenceStack = new CyclicReferenceStack(); $this->debugLog = new Logger($this->cyclicReferenceStack); + self::$referenceHelper = ReferenceHelper::getInstance(); } - private static function loadLocales() + private static function loadLocales(): void { $localeFileDirectory = __DIR__ . '/locale/'; foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) { @@ -2242,7 +2700,7 @@ class Calculation * * @return Calculation */ - public static function getInstance(Spreadsheet $spreadsheet = null) + public static function getInstance(?Spreadsheet $spreadsheet = null) { if ($spreadsheet !== null) { $instance = $spreadsheet->getCalculationEngine(); @@ -2262,7 +2720,7 @@ class Calculation * Flush the calculation cache for any existing instance of this class * but only if a Calculation instance exists. */ - public function flushInstance() + public function flushInstance(): void { $this->clearCalculationCache(); $this->clearBranchStore(); @@ -2280,8 +2738,6 @@ class Calculation /** * __clone implementation. Cloning should not be allowed in a Singleton! - * - * @throws Exception */ final public function __clone() { @@ -2317,9 +2773,11 @@ class Calculation */ public static function setArrayReturnType($returnType) { - if (($returnType == self::RETURN_ARRAY_AS_VALUE) || + if ( + ($returnType == self::RETURN_ARRAY_AS_VALUE) || ($returnType == self::RETURN_ARRAY_AS_ERROR) || - ($returnType == self::RETURN_ARRAY_AS_ARRAY)) { + ($returnType == self::RETURN_ARRAY_AS_ARRAY) + ) { self::$returnArrayAsType = $returnType; return true; @@ -2353,7 +2811,7 @@ class Calculation * * @param bool $pValue */ - public function setCalculationCacheEnabled($pValue) + public function setCalculationCacheEnabled($pValue): void { $this->calculationCacheEnabled = $pValue; $this->clearCalculationCache(); @@ -2362,7 +2820,7 @@ class Calculation /** * Enable calculation cache. */ - public function enableCalculationCache() + public function enableCalculationCache(): void { $this->setCalculationCacheEnabled(true); } @@ -2370,7 +2828,7 @@ class Calculation /** * Disable calculation cache. */ - public function disableCalculationCache() + public function disableCalculationCache(): void { $this->setCalculationCacheEnabled(false); } @@ -2378,7 +2836,7 @@ class Calculation /** * Clear calculation cache. */ - public function clearCalculationCache() + public function clearCalculationCache(): void { $this->calculationCache = []; } @@ -2388,7 +2846,7 @@ class Calculation * * @param string $worksheetName */ - public function clearCalculationCacheForWorksheet($worksheetName) + public function clearCalculationCacheForWorksheet($worksheetName): void { if (isset($this->calculationCache[$worksheetName])) { unset($this->calculationCache[$worksheetName]); @@ -2401,7 +2859,7 @@ class Calculation * @param string $fromWorksheetName * @param string $toWorksheetName */ - public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName) + public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName): void { if (isset($this->calculationCache[$fromWorksheetName])) { $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName]; @@ -2412,25 +2870,24 @@ class Calculation /** * Enable/disable calculation cache. * - * @param bool $pValue * @param mixed $enabled */ - public function setBranchPruningEnabled($enabled) + public function setBranchPruningEnabled($enabled): void { $this->branchPruningEnabled = $enabled; } - public function enableBranchPruning() + public function enableBranchPruning(): void { $this->setBranchPruningEnabled(true); } - public function disableBranchPruning() + public function disableBranchPruning(): void { $this->setBranchPruningEnabled(false); } - public function clearBranchStore() + public function clearBranchStore(): void { $this->branchStoreKeyCounter = 0; } @@ -2546,11 +3003,11 @@ class Calculation for ($i = 0; $i < $strlen; ++$i) { $chr = mb_substr($formula, $i, 1); switch ($chr) { - case '{': + case self::FORMULA_OPEN_FUNCTION_BRACE: $inBraces = true; break; - case '}': + case self::FORMULA_CLOSE_FUNCTION_BRACE: $inBraces = false; break; @@ -2579,10 +3036,10 @@ class Calculation if (self::$localeLanguage !== 'en_us') { $inBraces = false; // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators - if (strpos($formula, '"') !== false) { + if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) { // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // the formula - $temp = explode('"', $formula); + $temp = explode(self::FORMULA_STRING_QUOTE, $formula); $i = false; foreach ($temp as &$value) { // Only count/replace in alternating array entries @@ -2593,7 +3050,7 @@ class Calculation } unset($value); // Then rebuild the formula string - $formula = implode('"', $temp); + $formula = implode(self::FORMULA_STRING_QUOTE, $temp); } else { // If there's no quoted strings, then we do a simple count/replace $formula = preg_replace($from, $to, $formula); @@ -2694,9 +3151,9 @@ class Calculation return $value; } // Return strings wrapped in quotes - return '"' . $value . '"'; - // Convert numeric errors to NaN error + return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE; } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) { + // Convert numeric errors to NaN error return Functions::NAN(); } @@ -2713,7 +3170,7 @@ class Calculation public static function unwrapResult($value) { if (is_string($value)) { - if ((isset($value[0])) && ($value[0] == '"') && (substr($value, -1) == '"')) { + if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) { return substr($value, 1, -1); } // Convert numeric errors to NAN error @@ -2730,11 +3187,9 @@ class Calculation * * @param Cell $pCell Cell to calculate * - * @throws Exception - * * @return mixed */ - public function calculate(Cell $pCell = null) + public function calculate(?Cell $pCell = null) { try { return $this->calculateCellValue($pCell); @@ -2749,11 +3204,9 @@ class Calculation * @param Cell $pCell Cell to calculate * @param bool $resetLog Flag indicating whether the debug log should be reset or not * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * * @return mixed */ - public function calculateCellValue(Cell $pCell = null, $resetLog = true) + public function calculateCellValue(?Cell $pCell = null, $resetLog = true) { if ($pCell === null) { return null; @@ -2843,7 +3296,7 @@ class Calculation } // Parse the formula and return the token stack - return $this->_parseFormula($formula); + return $this->internalParseFormula($formula); } /** @@ -2853,24 +3306,22 @@ class Calculation * @param string $cellID Address of the cell to calculate * @param Cell $pCell Cell to calculate * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * * @return mixed */ - public function calculateFormula($formula, $cellID = null, Cell $pCell = null) + public function calculateFormula($formula, $cellID = null, ?Cell $pCell = null) { // Initialise the logging settings $this->formulaError = null; $this->debugLog->clearLog(); $this->cyclicReferenceStack->clear(); + $resetCache = $this->getCalculationCacheEnabled(); if ($this->spreadsheet !== null && $cellID === null && $pCell === null) { $cellID = 'A1'; $pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID); } else { // Disable calculation cacheing because it only applies to cell calculations, not straight formulae // But don't actually flush any cache - $resetCache = $this->getCalculationCacheEnabled(); $this->calculationCacheEnabled = false; } @@ -2916,7 +3367,7 @@ class Calculation * @param string $cellReference * @param mixed $cellValue */ - public function saveValueToCache($cellReference, $cellValue) + public function saveValueToCache($cellReference, $cellValue): void { if ($this->calculationCacheEnabled) { $this->calculationCache[$cellReference] = $cellValue; @@ -2930,11 +3381,9 @@ class Calculation * @param string $cellID The ID (e.g. A3) of the cell that we are calculating * @param Cell $pCell Cell to calculate * - * @throws Exception - * * @return mixed */ - public function _calculateFormulaValue($formula, $cellID = null, Cell $pCell = null) + public function _calculateFormulaValue($formula, $cellID = null, ?Cell $pCell = null) { $cellValue = null; @@ -2965,6 +3414,7 @@ class Calculation if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) { return $cellValue; } + $this->debugLog->writeDebugLog('Evaluating formula for cell ', $wsCellReference); if (($wsTitle[0] !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) { if ($this->cyclicFormulaCount <= 0) { @@ -2986,9 +3436,10 @@ class Calculation } } + $this->debugLog->writeDebugLog('Formula for cell ', $wsCellReference, ' is ', $formula); // Parse the formula onto the token stack and calculate the value $this->cyclicReferenceStack->push($wsCellReference); - $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell); + $cellValue = $this->processTokenStack($this->internalParseFormula($formula, $pCell), $cellID, $pCell); $this->cyclicReferenceStack->pop(); // Save to calculation cache @@ -3080,7 +3531,7 @@ class Calculation * @param int $matrix2Rows Row size of second matrix operand * @param int $matrix2Columns Column size of second matrix operand */ - private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) + private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void { if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) { if ($matrix2Rows < $matrix1Rows) { @@ -3123,7 +3574,7 @@ class Calculation * @param int $matrix2Rows Row size of second matrix operand * @param int $matrix2Columns Column size of second matrix operand */ - private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns) + private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void { if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) { if ($matrix2Columns < $matrix1Columns) { @@ -3188,8 +3639,8 @@ class Calculation } return '{ ' . implode($rpad, $returnMatrix) . ' }'; - } elseif (is_string($value) && (trim($value, '"') == $value)) { - return '"' . $value . '"'; + } elseif (is_string($value) && (trim($value, self::FORMULA_STRING_QUOTE) == $value)) { + return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE; } elseif (is_bool($value)) { return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; } @@ -3239,38 +3690,38 @@ class Calculation /** * @param string $formula * - * @return string + * @return false|string False indicates an error */ private function convertMatrixReferences($formula) { - static $matrixReplaceFrom = ['{', ';', '}']; + static $matrixReplaceFrom = [self::FORMULA_OPEN_FUNCTION_BRACE, ';', self::FORMULA_CLOSE_FUNCTION_BRACE]; static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))']; // Convert any Excel matrix references to the MKMATRIX() function - if (strpos($formula, '{') !== false) { + if (strpos($formula, self::FORMULA_OPEN_FUNCTION_BRACE) !== false) { // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators - if (strpos($formula, '"') !== false) { + if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) { // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // the formula - $temp = explode('"', $formula); + $temp = explode(self::FORMULA_STRING_QUOTE, $formula); // Open and Closed counts used for trapping mismatched braces in the formula $openCount = $closeCount = 0; $i = false; foreach ($temp as &$value) { // Only count/replace in alternating array entries if ($i = !$i) { - $openCount += substr_count($value, '{'); - $closeCount += substr_count($value, '}'); + $openCount += substr_count($value, self::FORMULA_OPEN_FUNCTION_BRACE); + $closeCount += substr_count($value, self::FORMULA_CLOSE_FUNCTION_BRACE); $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value); } } unset($value); // Then rebuild the formula string - $formula = implode('"', $temp); + $formula = implode(self::FORMULA_STRING_QUOTE, $temp); } else { // If there's no quoted strings, then we do a simple count/replace - $openCount = substr_count($formula, '{'); - $closeCount = substr_count($formula, '}'); + $openCount = substr_count($formula, self::FORMULA_OPEN_FUNCTION_BRACE); + $closeCount = substr_count($formula, self::FORMULA_CLOSE_FUNCTION_BRACE); $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula); } // Trap for mismatched braces and trigger an appropriate error @@ -3332,11 +3783,10 @@ class Calculation /** * @param string $formula - * @param null|\PhpOffice\PhpSpreadsheet\Cell\Cell $pCell * * @return bool */ - private function _parseFormula($formula, Cell $pCell = null) + private function internalParseFormula($formula, ?Cell $pCell = null) { if (($formula = $this->convertMatrixReferences(trim($formula))) === false) { return false; @@ -3351,18 +3801,18 @@ class Calculation '|' . self::CALCULATION_REGEXP_NUMBER . '|' . self::CALCULATION_REGEXP_STRING . '|' . self::CALCULATION_REGEXP_OPENBRACE . - '|' . self::CALCULATION_REGEXP_NAMEDRANGE . + '|' . self::CALCULATION_REGEXP_DEFINEDNAME . '|' . self::CALCULATION_REGEXP_ERROR . - ')/si'; + ')/sui'; // Start with initialisation $index = 0; $stack = new Stack(); $output = []; $expectingOperator = false; // We use this test in syntax-checking the expression to determine when a - // - is a negation or + is a positive operator rather than an operation + // - is a negation or + is a positive operator rather than an operation $expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand - // should be null in a function call + // should be null in a function call // IF branch pruning // currently pending storeKey (last item of the storeKeysStack @@ -3411,13 +3861,12 @@ class Calculation } $opCharacter = $formula[$index]; // Get the first character of the value at the current index position + if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) { $opCharacter .= $formula[++$index]; } - // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match); - if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus? // Put a negation on the stack $stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); @@ -3430,11 +3879,13 @@ class Calculation ++$index; // Drop the redundant plus symbol } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) { // We have to explicitly deny a tilde or pipe, because they are legal return $this->raiseFormulaError("Formula Error: Illegal character '~'"); // on the stack but not in the input expression - } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack? - while ($stack->count() > 0 && + } elseif ((isset(self::$operators[$opCharacter]) || $isOperandOrFunction) && $expectingOperator) { // Are we putting an operator on the stack? + while ( + $stack->count() > 0 && ($o2 = $stack->last()) && isset(self::$operators[$o2['value']]) && - @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { + @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']]) + ) { $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output } @@ -3456,10 +3907,10 @@ class Calculation // Branch pruning we decrease the depth whether is it a function // call or a parenthesis if (!empty($pendingStoreKey)) { - $parenthesisDepthMap[$pendingStoreKey] -= 1; + --$parenthesisDepthMap[$pendingStoreKey]; } - if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function? + if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) { // Did this parenthesis just close a function? if (!empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == -1) { // we are closing an IF( if ($d['value'] != 'IF(') { @@ -3470,7 +3921,7 @@ class Calculation } $expectingThenMap[$pendingStoreKey] = false; $expectingElseMap[$pendingStoreKey] = false; - $parenthesisDepthMap[$pendingStoreKey] -= 1; + --$parenthesisDepthMap[$pendingStoreKey]; array_pop($pendingStoreKeysStack); unset($pendingStoreKey); } @@ -3535,7 +3986,8 @@ class Calculation } ++$index; } elseif ($opCharacter == ',') { // Is this the separator for function arguments? - if (!empty($pendingStoreKey) && + if ( + !empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == 0 ) { // We must go to the IF next argument @@ -3562,7 +4014,7 @@ class Calculation } // make sure there was a function $d = $stack->last(2); - if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { + if (!preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $d['value'], $matches)) { return $this->raiseFormulaError('Formula Error: Unexpected ,'); } $d = $stack->pop(); @@ -3576,7 +4028,7 @@ class Calculation ++$index; } elseif ($opCharacter == '(' && !$expectingOperator) { if (!empty($pendingStoreKey)) { // Branch pruning: we go deeper - $parenthesisDepthMap[$pendingStoreKey] += 1; + ++$parenthesisDepthMap[$pendingStoreKey]; } $stack->push('Brace', '(', null, $currentCondition, $currentOnlyIf, $currentOnlyIf); ++$index; @@ -3585,42 +4037,40 @@ class Calculation $expectingOperand = false; $val = $match[1]; $length = strlen($val); - - if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) { + if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) { $val = preg_replace('/\s/u', '', $val); if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function $valToUpper = strtoupper($val); - // here $matches[1] will contain values like "IF" - // and $val "IF(" - if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if - $pendingStoreKey = $this->getUnusedBranchStoreKey(); - $pendingStoreKeysStack[] = $pendingStoreKey; - $expectingConditionMap[$pendingStoreKey] = true; - $parenthesisDepthMap[$pendingStoreKey] = 0; - } else { // this is not a if but we good deeper - if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) { - $parenthesisDepthMap[$pendingStoreKey] += 1; - } - } - - $stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); - // tests if the function is closed right after opening - $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch); - if ($ax) { - $stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); - $expectingOperator = true; - } else { - $stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); - $expectingOperator = false; - } - $stack->push('Brace', '('); - } else { // it's a var w/ implicit multiplication - $output[] = ['type' => 'Value', 'value' => $matches[1], 'reference' => null]; + } else { + $valToUpper = 'NAME.ERROR('; } + // here $matches[1] will contain values like "IF" + // and $val "IF(" + if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if + $pendingStoreKey = $this->getUnusedBranchStoreKey(); + $pendingStoreKeysStack[] = $pendingStoreKey; + $expectingConditionMap[$pendingStoreKey] = true; + $parenthesisDepthMap[$pendingStoreKey] = 0; + } else { // this is not an if but we go deeper + if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) { + ++$parenthesisDepthMap[$pendingStoreKey]; + } + } + + $stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); + // tests if the function is closed right after opening + $ax = preg_match('/^\s*\)/u', substr($formula, $index + $length)); + if ($ax) { + $stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); + $expectingOperator = true; + } else { + $stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); + $expectingOperator = false; + } + $stack->push('Brace', '('); } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) { // Watch for this case-change when modifying to allow cell references in different worksheets... // Should only be applied to the actual cell column, not the worksheet name - // If the last entry on the stack was a : operator, then we have a cell range reference $testPrevOp = $stack->last(1); if ($testPrevOp !== null && $testPrevOp['value'] == ':') { @@ -3628,13 +4078,17 @@ class Calculation if ($matches[2] == '') { // Otherwise, we 'inherit' the worksheet reference from the start cell reference // The start of the cell range reference should be the last entry in $output - $startCellRef = $output[count($output) - 1]['value']; - preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $startCellRef, $startMatches); - if ($startMatches[2] > '') { - $val = $startMatches[2] . '!' . $val; + $rangeStartCellRef = $output[count($output) - 1]['value']; + preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $rangeStartCellRef, $rangeStartMatches); + if ($rangeStartMatches[2] > '') { + $val = $rangeStartMatches[2] . '!' . $val; } } else { - return $this->raiseFormulaError('3D Range references are not yet supported'); + $rangeStartCellRef = $output[count($output) - 1]['value']; + preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $rangeStartCellRef, $rangeStartMatches); + if ($rangeStartMatches[2] !== $matches[2]) { + return $this->raiseFormulaError('3D Range references are not yet supported'); + } } } @@ -3661,14 +4115,18 @@ class Calculation if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) { $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef); } - if ((is_int($startRowColRef)) && (ctype_digit($val)) && - ($startRowColRef <= 1048576) && ($val <= 1048576)) { + if ( + (is_int($startRowColRef)) && (ctype_digit($val)) && + ($startRowColRef <= 1048576) && ($val <= 1048576) + ) { // Row range $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007 $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef; $val = $rangeWS2 . $endRowColRef . $val; - } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && - (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) { + } elseif ( + (ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && + (strlen($startRowColRef) <= 3) && (strlen($val) <= 3) + ) { // Column range $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007 $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1'; @@ -3677,9 +4135,11 @@ class Calculation } $localeConstant = false; - if ($opCharacter == '"') { + $stackItemType = 'Value'; + $stackItemReference = null; + if ($opCharacter == self::FORMULA_STRING_QUOTE) { // UnEscape any quotes within the string - $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val))); + $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val))); } elseif (is_numeric($val)) { if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { $val = (float) $val; @@ -3687,12 +4147,17 @@ class Calculation $val = (int) $val; } } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) { + $stackItemType = 'Constant'; $excelConstant = trim(strtoupper($val)); $val = self::$excelConstants[$excelConstant]; } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) { + $stackItemType = 'Constant'; $val = self::$excelConstants[$localeConstant]; + } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) { + $stackItemType = 'Defined Name'; + $stackItemReference = $val; } - $details = $stack->getStackItem('Value', $val, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); + $details = $stack->getStackItem($stackItemType, $val, $stackItemReference, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); if ($localeConstant) { $details['localeValue'] = $localeConstant; } @@ -3712,7 +4177,7 @@ class Calculation } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) { return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'"); } else { // I don't even want to know what you did to get here - return $this->raiseFormulaError('Formula Error: An unexpected error occured'); + return $this->raiseFormulaError('Formula Error: An unexpected error occurred'); } // Test for end of formula string if ($index == strlen($formula)) { @@ -3728,18 +4193,28 @@ class Calculation while (($formula[$index] == "\n") || ($formula[$index] == "\r")) { ++$index; } + if ($formula[$index] == ' ') { while ($formula[$index] == ' ') { ++$index; } + // If we're expecting an operator, but only have a space between the previous and next operands (and both are // Cell References) then we have an INTERSECTION operator - if (($expectingOperator) && (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) && - ($output[count($output) - 1]['type'] == 'Cell Reference')) { - while ($stack->count() > 0 && + if ( + ($expectingOperator) && + ((preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) && + ($output[count($output) - 1]['type'] == 'Cell Reference') || + (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match)) && + ($output[count($output) - 1]['type'] == 'Defined Name' || $output[count($output) - 1]['type'] == 'Value') + ) + ) { + while ( + $stack->count() > 0 && ($o2 = $stack->last()) && isset(self::$operators[$o2['value']]) && - @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) { + @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']]) + ) { $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output } $stack->push('Binary Operator', '|'); // Put an Intersect Operator on the stack @@ -3779,11 +4254,10 @@ class Calculation /** * @param mixed $tokens * @param null|string $cellID - * @param null|Cell $pCell * * @return bool */ - private function processTokenStack($tokens, $cellID = null, Cell $pCell = null) + private function processTokenStack($tokens, $cellID = null, ?Cell $pCell = null) { if ($tokens == false) { return false; @@ -3799,7 +4273,6 @@ class Calculation $fakedForBranchPruning = []; // help us to know when pruning ['branchTestId' => true/false] $branchStore = []; - // Loop through each token in turn foreach ($tokens as $tokenData) { $token = $tokenData['value']; @@ -3816,7 +4289,8 @@ class Calculation $storeValue = end($wrappedItem); } - if (isset($storeValue) + if ( + isset($storeValue) && ( !$storeValueAsBool || Functions::isError($storeValue) @@ -3850,7 +4324,8 @@ class Calculation $wrappedItem = end($storeValue); $storeValue = end($wrappedItem); } - if (isset($storeValue) + if ( + isset($storeValue) && ( $storeValueAsBool || Functions::isError($storeValue) @@ -4021,7 +4496,7 @@ class Calculation $result = '#VALUE!'; } } else { - $result = '"' . str_replace('""', '"', self::unwrapResult($operand1) . self::unwrapResult($operand2)) . '"'; + $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE; } $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); $stack->push('Value', $result); @@ -4041,9 +4516,15 @@ class Calculation $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]); } } - $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); - $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); - $stack->push('Value', $cellIntersect, $cellRef); + if (count(Functions::flattenArray($cellIntersect)) === 0) { + $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); + $stack->push('Error', Functions::null(), null); + } else { + $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . + Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); + $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); + $stack->push('Value', $cellIntersect, $cellRef); + } break; } @@ -4155,17 +4636,10 @@ class Calculation } // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on - } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) { + } elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $token, $matches)) { if ($pCellParent) { $pCell->attach($pCellParent); } - if (($cellID == 'AC99') || (isset($pCell) && $pCell->getCoordinate() == 'AC99')) { - if (defined('RESOLVING')) { - define('RESOLVING2', true); - } else { - define('RESOLVING', true); - } - } $functionName = $matches[1]; $argCount = $stack->pop(); @@ -4188,9 +4662,11 @@ class Calculation for ($i = 0; $i < $argCount; ++$i) { $arg = $stack->pop(); $a = $argCount - $i - 1; - if (($passByReference) && + if ( + ($passByReference) && (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) && - (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) { + (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a]) + ) { if ($arg['reference'] === null) { $args[] = $cellID; if ($functionName != 'MKMATRIX') { @@ -4254,25 +4730,30 @@ class Calculation $branchStore[$storeKey] = self::$excelConstants[$excelConstant]; } $this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant])); - } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) { + } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) { $stack->push('Value', $token); if (isset($storeKey)) { $branchStore[$storeKey] = $token; } - // if the token is a named range, push the named range name onto the stack - } elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) { - $namedRange = $matches[6]; - $this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange); + // if the token is a named range or formula, evaluate it and push the result onto the stack + } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) { + $definedName = $matches[6]; + if ($pCell === null || $pCellWorksheet === null) { + return $this->raiseFormulaError("undefined name '$token'"); + } - $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false); - $pCell->attach($pCellParent); - $this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue)); - $stack->push('Named Range', $cellValue, $namedRange); + $this->debugLog->writeDebugLog('Evaluating Defined Name ', $definedName); + $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet); + if ($namedRange === null) { + return $this->raiseFormulaError("undefined name '$definedName'"); + } + + $result = $this->evaluateDefinedName($pCell, $namedRange, $pCellWorksheet, $stack); if (isset($storeKey)) { - $branchStore[$storeKey] = $cellValue; + $branchStore[$storeKey] = $result; } } else { - return $this->raiseFormulaError("undefined variable '$token'"); + return $this->raiseFormulaError("undefined name '$token'"); } } } @@ -4299,7 +4780,7 @@ class Calculation if (is_string($operand)) { // We only need special validations for the operand if it is a string // Start by stripping off the quotation marks we use to identify true excel string values internally - if ($operand > '' && $operand[0] == '"') { + if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) { $operand = self::unwrapResult($operand); } // If the string is a numeric value, we treat it as a numeric, so no further testing @@ -4312,7 +4793,7 @@ class Calculation return false; } elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) { // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations - $stack->push('Value', '#VALUE!'); + $stack->push('Error', '#VALUE!'); $this->debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!')); return false; @@ -4329,7 +4810,6 @@ class Calculation * @param mixed $operand1 * @param mixed $operand2 * @param string $operation - * @param Stack $stack * @param bool $recursingArrays * * @return mixed @@ -4373,10 +4853,10 @@ class Calculation } // Simple validate the two operands if they are string values - if (is_string($operand1) && $operand1 > '' && $operand1[0] == '"') { + if (is_string($operand1) && $operand1 > '' && $operand1[0] == self::FORMULA_STRING_QUOTE) { $operand1 = self::unwrapResult($operand1); } - if (is_string($operand2) && $operand2 > '' && $operand2[0] == '"') { + if (is_string($operand2) && $operand2 > '' && $operand2[0] == self::FORMULA_STRING_QUOTE) { $operand2 = self::unwrapResult($operand2); } @@ -4515,9 +4995,11 @@ class Calculation $result = '#VALUE!'; } } else { - if ((Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) && + if ( + (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) && ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) || - (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))) { + (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0)) + ) { $result = Functions::VALUE(); } else { // If we're dealing with non-matrix operations, execute the necessary operation @@ -4541,17 +5023,17 @@ class Calculation case '/': if ($operand2 == 0) { // Trap for Divide by Zero error - $stack->push('Value', '#DIV/0!'); + $stack->push('Error', '#DIV/0!'); $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!')); return false; } - $result = $operand1 / $operand2; + $result = $operand1 / $operand2; break; // Power case '^': - $result = pow($operand1, $operand2); + $result = $operand1 ** $operand2; break; } @@ -4588,7 +5070,7 @@ class Calculation * * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. */ - public function extractCellRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true) + public function extractCellRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true) { // Return value $returnValue = []; @@ -4641,7 +5123,7 @@ class Calculation * * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned. */ - public function extractNamedRange(&$pRange = 'A1', Worksheet $pSheet = null, $resetLog = true) + public function extractNamedRange(&$pRange = 'A1', ?Worksheet $pSheet = null, $resetLog = true) { // Return value $returnValue = []; @@ -4654,21 +5136,21 @@ class Calculation } // Named range? - $namedRange = NamedRange::resolveRange($pRange, $pSheet); - if ($namedRange !== null) { - $pSheet = $namedRange->getWorksheet(); - $pRange = $namedRange->getRange(); - $splitRange = Coordinate::splitRange($pRange); - // Convert row and column references - if (ctype_alpha($splitRange[0][0])) { - $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow(); - } elseif (ctype_digit($splitRange[0][0])) { - $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1]; - } - } else { + $namedRange = DefinedName::resolveName($pRange, $pSheet); + if ($namedRange === null) { return Functions::REF(); } + $pSheet = $namedRange->getWorksheet(); + $pRange = $namedRange->getValue(); + $splitRange = Coordinate::splitRange($pRange); + // Convert row and column references + if (ctype_alpha($splitRange[0][0])) { + $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow(); + } elseif (ctype_digit($splitRange[0][0])) { + $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1]; + } + // Extract range $aReferences = Coordinate::extractAllCellReferencesInRange($pRange); if (!isset($aReferences[1])) { @@ -4741,21 +5223,19 @@ class Calculation /** * Add cell reference if needed while making sure that it is the last argument. * - * @param array $args * @param bool $passCellReference * @param array|string $functionCall - * @param null|Cell $pCell * * @return array */ - private function addCellReference(array $args, $passCellReference, $functionCall, Cell $pCell = null) + private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $pCell = null) { if ($passCellReference) { if (is_array($functionCall)) { $className = $functionCall[0]; $methodName = $functionCall[1]; - $reflectionMethod = new \ReflectionMethod($className, $methodName); + $reflectionMethod = new ReflectionMethod($className, $methodName); $argumentCount = count($reflectionMethod->getParameters()); while (count($args) < $argumentCount - 1) { $args[] = null; @@ -4789,4 +5269,59 @@ class Calculation return '[ ' . implode(' | ', $tokensStr) . ' ]'; } + + /** + * @return mixed|string + */ + private function evaluateDefinedName(Cell $pCell, DefinedName $namedRange, Worksheet $pCellWorksheet, Stack $stack) + { + $definedNameScope = $namedRange->getScope(); + if ($definedNameScope !== null && $definedNameScope !== $pCellWorksheet) { + // The defined name isn't in our current scope, so #REF + $result = Functions::REF(); + $stack->push('Error', $result, $namedRange->getName()); + + return $result; + } + + $definedNameValue = $namedRange->getValue(); + $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range'; + $definedNameWorksheet = $namedRange->getWorksheet(); + + if ($definedNameValue[0] !== '=') { + $definedNameValue = '=' . $definedNameValue; + } + + $this->debugLog->writeDebugLog("Defined Name is a {$definedNameType} with a value of {$definedNameValue}"); + + $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $pCellWorksheet) + ? $definedNameWorksheet->getCell('A1') + : $pCell; + $recursiveCalculationCellAddress = $recursiveCalculationCell !== null + ? $recursiveCalculationCell->getCoordinate() + : null; + + // Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns + $definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet( + $definedNameValue, + Coordinate::columnIndexFromString($pCell->getColumn()) - 1, + $pCell->getRow() - 1 + ); + + $this->debugLog->writeDebugLog("Value adjusted for relative references is {$definedNameValue}"); + + $recursiveCalculator = new self($this->spreadsheet); + $recursiveCalculator->getDebugLog()->setWriteDebugLog($this->getDebugLog()->getWriteDebugLog()); + $recursiveCalculator->getDebugLog()->setEchoDebugLog($this->getDebugLog()->getEchoDebugLog()); + $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell); + + if ($this->getDebugLog()->getWriteDebugLog()) { + $this->debugLog->mergeDebugLog(array_slice($recursiveCalculator->getDebugLog()->getLog(), 3)); + $this->debugLog->writeDebugLog("Evaluation Result for Named {$definedNameType} {$namedRange->getName()} is {$this->showTypeDetails($result)}"); + } + + $stack->push('Defined Name', $result, $namedRange->getName()); + + return $result; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php index 7574cb4766a..96bb72ad4cb 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Category.php @@ -16,4 +16,5 @@ abstract class Category const CATEGORY_MATH_AND_TRIG = 'Math and Trig'; const CATEGORY_STATISTICAL = 'Statistical'; const CATEGORY_TEXT_AND_DATA = 'Text and Data'; + const CATEGORY_WEB = 'Web'; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php index d31b00dd7df..2ba4af2dc4c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php @@ -130,8 +130,6 @@ class Database * Excel Function: * DAVERAGE(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -174,8 +172,6 @@ class Database * Excel Function: * DAVERAGE(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -217,8 +213,6 @@ class Database * Excel Function: * DCOUNTA(database,[field],criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -269,8 +263,6 @@ class Database * Excel Function: * DGET(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -313,8 +305,6 @@ class Database * Excel Function: * DMAX(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -354,8 +344,6 @@ class Database * Excel Function: * DMIN(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -394,8 +382,6 @@ class Database * Excel Function: * DPRODUCT(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -435,8 +421,6 @@ class Database * Excel Function: * DSTDEV(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -476,8 +460,6 @@ class Database * Excel Function: * DSTDEVP(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -516,8 +498,6 @@ class Database * Excel Function: * DSUM(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -557,8 +537,6 @@ class Database * Excel Function: * DVAR(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -574,7 +552,7 @@ class Database * the column label in which you specify a condition for the * column. * - * @return float + * @return float|string (string if result is an error) */ public static function DVAR($database, $field, $criteria) { @@ -598,8 +576,6 @@ class Database * Excel Function: * DVARP(database,field,criteria) * - * @category Database Functions - * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related * information are records, and columns of data are fields. The @@ -615,7 +591,7 @@ class Database * the column label in which you specify a condition for the * column. * - * @return float + * @return float|string (string if result is an error) */ public static function DVARP($database, $field, $criteria) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php index 796e79593df..4c2b108ad9f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; +use DateTimeImmutable; +use DateTimeInterface; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; @@ -59,18 +61,14 @@ class DateTime /** * getDateValue. * - * @param string $dateValue + * @param mixed $dateValue * * @return mixed Excel date/time serial value, or string if error */ public static function getDateValue($dateValue) { if (!is_numeric($dateValue)) { - if ((is_string($dateValue)) && - (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) { - return Functions::VALUE(); - } - if ((is_object($dateValue)) && ($dateValue instanceof \DateTimeInterface)) { + if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) { $dateValue = Date::PHPToExcel($dateValue); } else { $saveReturnDateType = Functions::getReturnDateType(); @@ -141,8 +139,6 @@ class DateTime * Excel Function: * NOW() * - * @category Date/Time Functions - * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ @@ -184,8 +180,6 @@ class DateTime * Excel Function: * TODAY() * - * @category Date/Time Functions - * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ @@ -229,8 +223,6 @@ class DateTime * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted, * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language. * - * @category Date/Time Functions - * * @param int $year The value of the year argument can include one to four digits. * Excel interprets the year argument according to the configured * date system: 1900 or 1904. @@ -282,9 +274,11 @@ class DateTime $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0; $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0; $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0; - if ((!is_numeric($year)) || + if ( + (!is_numeric($year)) || (!is_numeric($month)) || - (!is_numeric($day))) { + (!is_numeric($day)) + ) { return Functions::VALUE(); } $year = (int) $year; @@ -343,8 +337,6 @@ class DateTime * Excel Function: * TIME(hour,minute,second) * - * @category Date/Time Functions - * * @param int $hour A number from 0 (zero) to 32767 representing the hour. * Any value greater than 23 will be divided by 24 and the remainder * will be treated as the hour value. For example, TIME(27,0,0) = @@ -456,8 +448,6 @@ class DateTime * Excel Function: * DATEVALUE(dateValue) * - * @category Date/Time Functions - * * @param string $dateValue Text that represents a date in a Microsoft Excel date format. * For example, "1/30/2008" or "30-Jan-2008" are text strings within * quotation marks that represent dates. Using the default date @@ -491,7 +481,7 @@ class DateTime $yearFound = true; } } - if ((count($t1) == 1) && (strpos($t, ':') != false)) { + if ((count($t1) == 1) && (strpos($t, ':') !== false)) { // We've been fed a time value without any date return 0.0; } elseif (count($t1) == 2) { @@ -499,7 +489,7 @@ class DateTime if ($yearFound) { array_unshift($t1, 1); } else { - if ($t1[1] > 29) { + if (is_numeric($t1[1]) && $t1[1] > 29) { $t1[1] += 1900; array_unshift($t1, 1); } else { @@ -591,8 +581,6 @@ class DateTime * Excel Function: * TIMEVALUE(timeValue) * - * @category Date/Time Functions - * * @param string $timeValue A text string that represents a time in any one of the Microsoft * Excel time formats; for example, "6:45 PM" and "18:45" text strings * within quotation marks that represent time. @@ -682,30 +670,19 @@ class DateTime $endMonths = $PHPEndDateObject->format('n'); $endYears = $PHPEndDateObject->format('Y'); + $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject); + switch ($unit) { case 'D': $retVal = (int) $difference; break; case 'M': - $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12); - // We're only interested in full months - if ($endDays < $startDays) { - --$retVal; - } + $retVal = (int) 12 * $PHPDiffDateObject->format('%y') + $PHPDiffDateObject->format('%m'); break; case 'Y': - $retVal = (int) ($endYears - $startYears); - // We're only interested in full months - if ($endMonths < $startMonths) { - --$retVal; - } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) { - // Remove start month - --$retVal; - // Remove end month - --$retVal; - } + $retVal = (int) $PHPDiffDateObject->format('%y'); break; case 'MD': @@ -715,19 +692,12 @@ class DateTime $adjustDays = $PHPEndDateObject->format('j'); $retVal += ($adjustDays - $startDays); } else { - $retVal = $endDays - $startDays; + $retVal = (int) $PHPDiffDateObject->format('%d'); } break; case 'YM': - $retVal = (int) ($endMonths - $startMonths); - if ($retVal < 0) { - $retVal += 12; - } - // We're only interested in full months - if ($endDays < $startDays) { - --$retVal; - } + $retVal = (int) $PHPDiffDateObject->format('%m'); break; case 'YD': @@ -770,11 +740,9 @@ class DateTime * Excel Function: * DAYS(endDate, startDate) * - * @category Date/Time Functions - * - * @param \DateTimeImmutable|float|int|string $endDate Excel date serial value (float), + * @param DateTimeImmutable|float|int|string $endDate Excel date serial value (float), * PHP date timestamp (integer), PHP DateTime object, or a standard date string - * @param \DateTimeImmutable|float|int|string $startDate Excel date serial value (float), + * @param DateTimeImmutable|float|int|string $startDate Excel date serial value (float), * PHP date timestamp (integer), PHP DateTime object, or a standard date string * * @return int|string Number of days between start date and end date or an error @@ -818,8 +786,6 @@ class DateTime * Excel Function: * DAYS360(startDate,endDate[,method]) * - * @category Date/Time Functions - * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), @@ -878,8 +844,8 @@ class DateTime * * Excel Function: * YEARFRAC(startDate,endDate[,method]) - * - * @category Date/Time Functions + * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html + * for description of algorithm used in Excel * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -892,7 +858,7 @@ class DateTime * 3 Actual/365 * 4 European 30/360 * - * @return float fraction of the year + * @return float|string fraction of the year, or a string containing an error */ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) { @@ -906,6 +872,11 @@ class DateTime if (is_string($endDate = self::getDateValue($endDate))) { return Functions::VALUE(); } + if ($startDate > $endDate) { + $temp = $startDate; + $startDate = $endDate; + $endDate = $temp; + } if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) { switch ($method) { @@ -916,46 +887,43 @@ class DateTime $startYear = self::YEAR($startDate); $endYear = self::YEAR($endDate); $years = $endYear - $startYear + 1; - $leapDays = 0; + $startMonth = self::MONTHOFYEAR($startDate); + $startDay = self::DAYOFMONTH($startDate); + $endMonth = self::MONTHOFYEAR($endDate); + $endDay = self::DAYOFMONTH($endDate); + $startMonthDay = 100 * $startMonth + $startDay; + $endMonthDay = 100 * $endMonth + $endDay; if ($years == 1) { if (self::isLeapYear($endYear)) { - $startMonth = self::MONTHOFYEAR($startDate); - $endMonth = self::MONTHOFYEAR($endDate); - $endDay = self::DAYOFMONTH($endDate); - if (($startMonth < 3) || - (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) { - $leapDays += 1; + $tmpCalcAnnualBasis = 366; + } else { + $tmpCalcAnnualBasis = 365; + } + } elseif ($years == 2 && $startMonthDay >= $endMonthDay) { + if (self::isLeapYear($startYear)) { + if ($startMonthDay <= 229) { + $tmpCalcAnnualBasis = 366; + } else { + $tmpCalcAnnualBasis = 365; } + } elseif (self::isLeapYear($endYear)) { + if ($endMonthDay >= 229) { + $tmpCalcAnnualBasis = 366; + } else { + $tmpCalcAnnualBasis = 365; + } + } else { + $tmpCalcAnnualBasis = 365; } } else { + $tmpCalcAnnualBasis = 0; for ($year = $startYear; $year <= $endYear; ++$year) { - if ($year == $startYear) { - $startMonth = self::MONTHOFYEAR($startDate); - $startDay = self::DAYOFMONTH($startDate); - if ($startMonth < 3) { - $leapDays += (self::isLeapYear($year)) ? 1 : 0; - } - } elseif ($year == $endYear) { - $endMonth = self::MONTHOFYEAR($endDate); - $endDay = self::DAYOFMONTH($endDate); - if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) { - $leapDays += (self::isLeapYear($year)) ? 1 : 0; - } - } else { - $leapDays += (self::isLeapYear($year)) ? 1 : 0; - } + $tmpCalcAnnualBasis += self::isLeapYear($year) ? 366 : 365; } - if ($years == 2) { - if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) { - $leapDays = 1; - } elseif ($days < 366) { - $years = 1; - } - } - $leapDays /= $years; + $tmpCalcAnnualBasis /= $years; } - return $days / (365 + $leapDays); + return $days / $tmpCalcAnnualBasis; case 2: return self::DATEDIF($startDate, $endDate) / 360; case 3: @@ -979,8 +947,6 @@ class DateTime * Excel Function: * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]]) * - * @category Date/Time Functions - * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), @@ -1059,8 +1025,6 @@ class DateTime * Excel Function: * WORKDAY(startDate,endDays[,holidays[,holiday[,...]]]) * - * @category Date/Time Functions - * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $endDays The number of nonweekend and nonholiday days before or after @@ -1273,6 +1237,36 @@ class DateTime return $DoW; } + const STARTWEEK_SUNDAY = 1; + const STARTWEEK_MONDAY = 2; + const STARTWEEK_MONDAY_ALT = 11; + const STARTWEEK_TUESDAY = 12; + const STARTWEEK_WEDNESDAY = 13; + const STARTWEEK_THURSDAY = 14; + const STARTWEEK_FRIDAY = 15; + const STARTWEEK_SATURDAY = 16; + const STARTWEEK_SUNDAY_ALT = 17; + const DOW_SUNDAY = 1; + const DOW_MONDAY = 2; + const DOW_TUESDAY = 3; + const DOW_WEDNESDAY = 4; + const DOW_THURSDAY = 5; + const DOW_FRIDAY = 6; + const DOW_SATURDAY = 7; + const STARTWEEK_MONDAY_ISO = 21; + const METHODARR = [ + self::STARTWEEK_SUNDAY => self::DOW_SUNDAY, + self::DOW_MONDAY, + self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY, + self::DOW_TUESDAY, + self::DOW_WEDNESDAY, + self::DOW_THURSDAY, + self::DOW_FRIDAY, + self::DOW_SATURDAY, + self::DOW_SUNDAY, + self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO, + ]; + /** * WEEKNUM. * @@ -1291,41 +1285,51 @@ class DateTime * @param int $method Week begins on Sunday or Monday * 1 or omitted Week begins on Sunday. * 2 Week begins on Monday. + * 11 Week begins on Monday. + * 12 Week begins on Tuesday. + * 13 Week begins on Wednesday. + * 14 Week begins on Thursday. + * 15 Week begins on Friday. + * 16 Week begins on Saturday. + * 17 Week begins on Sunday. + * 21 ISO (Jan. 4 is week 1, begins on Monday). * * @return int|string Week Number */ - public static function WEEKNUM($dateValue = 1, $method = 1) + public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY) { $dateValue = Functions::flattenSingleValue($dateValue); $method = Functions::flattenSingleValue($method); if (!is_numeric($method)) { return Functions::VALUE(); - } elseif (($method < 1) || ($method > 2)) { - return Functions::NAN(); } - $method = floor($method); + $method = (int) $method; + if (!array_key_exists($method, self::METHODARR)) { + return Functions::NaN(); + } + $method = self::METHODARR[$method]; - if ($dateValue === null) { - $dateValue = 1; - } elseif (is_string($dateValue = self::getDateValue($dateValue))) { + $dateValue = self::getDateValue($dateValue); + if (is_string($dateValue)) { return Functions::VALUE(); - } elseif ($dateValue < 0.0) { + } + if ($dateValue < 0.0) { return Functions::NAN(); } // Execute function $PHPDateObject = Date::excelToDateTimeObject($dateValue); + if ($method == self::STARTWEEK_MONDAY_ISO) { + return (int) $PHPDateObject->format('W'); + } $dayOfYear = $PHPDateObject->format('z'); $PHPDateObject->modify('-' . $dayOfYear . ' days'); $firstDayOfFirstWeek = $PHPDateObject->format('w'); $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7; - $interval = $dayOfYear - $daysInFirstWeek; - $weekOfYear = floor($interval / 7) + 1; - - if ($daysInFirstWeek) { - ++$weekOfYear; - } + $daysInFirstWeek += 7 * !$daysInFirstWeek; + $endFirstWeek = $daysInFirstWeek - 1; + $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7); return (int) $weekOfYear; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php index 5a54d83ac03..b688e056341 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php @@ -26,7 +26,7 @@ class CyclicReferenceStack * * @param mixed $value */ - public function push($value) + public function push($value): void { $this->stack[$value] = $value; } @@ -56,7 +56,7 @@ class CyclicReferenceStack /** * Clear the stack. */ - public function clear() + public function clear(): void { $this->stack = []; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php index 6793dade78e..3c0f2377e4f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php @@ -39,8 +39,6 @@ class Logger /** * Instantiate a Calculation engine logger. - * - * @param CyclicReferenceStack $stack */ public function __construct(CyclicReferenceStack $stack) { @@ -52,7 +50,7 @@ class Logger * * @param bool $pValue */ - public function setWriteDebugLog($pValue) + public function setWriteDebugLog($pValue): void { $this->writeDebugLog = $pValue; } @@ -72,7 +70,7 @@ class Logger * * @param bool $pValue */ - public function setEchoDebugLog($pValue) + public function setEchoDebugLog($pValue): void { $this->echoDebugLog = $pValue; } @@ -90,11 +88,11 @@ class Logger /** * Write an entry to the calculation engine debug log. */ - public function writeDebugLog(...$args) + public function writeDebugLog(...$args): void { // Only write the debug log if logging is enabled if ($this->writeDebugLog) { - $message = implode($args); + $message = implode('', $args); $cellReference = implode(' -> ', $this->cellStack->showStack()); if ($this->echoDebugLog) { echo $cellReference, @@ -108,10 +106,24 @@ class Logger } } + /** + * Write a series of entries to the calculation engine debug log. + * + * @param string[] $args + */ + public function mergeDebugLog(array $args): void + { + if ($this->writeDebugLog) { + foreach ($args as $entry) { + $this->writeDebugLog($entry); + } + } + } + /** * Clear the calculation engine debug log. */ - public function clearLog() + public function clearLog(): void { $this->debugLog = []; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php index 458d0e86733..57116f28f53 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use Complex\Complex; use Complex\Exception as ComplexException; +use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ConvertUOM; class Engineering { @@ -12,710 +13,6 @@ class Engineering */ const EULER = 2.71828182845904523536; - /** - * Details of the Units of measure that can be used in CONVERTUOM(). - * - * @var mixed[] - */ - private static $conversionUnits = [ - 'g' => ['Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true], - 'sg' => ['Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false], - 'lbm' => ['Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false], - 'u' => ['Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true], - 'ozm' => ['Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false], - 'm' => ['Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true], - 'mi' => ['Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false], - 'Nmi' => ['Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false], - 'in' => ['Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false], - 'ft' => ['Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false], - 'yd' => ['Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false], - 'ang' => ['Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true], - 'Pica' => ['Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false], - 'yr' => ['Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false], - 'day' => ['Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false], - 'hr' => ['Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false], - 'mn' => ['Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false], - 'sec' => ['Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true], - 'Pa' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true], - 'p' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true], - 'atm' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true], - 'at' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true], - 'mmHg' => ['Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true], - 'N' => ['Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true], - 'dyn' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true], - 'dy' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true], - 'lbf' => ['Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false], - 'J' => ['Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true], - 'e' => ['Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true], - 'c' => ['Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true], - 'cal' => ['Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true], - 'eV' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true], - 'ev' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true], - 'HPh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false], - 'hh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false], - 'Wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true], - 'wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true], - 'flb' => ['Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false], - 'BTU' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false], - 'btu' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false], - 'HP' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false], - 'h' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false], - 'W' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true], - 'w' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true], - 'T' => ['Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true], - 'ga' => ['Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true], - 'C' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false], - 'cel' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false], - 'F' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false], - 'fah' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false], - 'K' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false], - 'kel' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false], - 'tsp' => ['Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false], - 'tbs' => ['Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false], - 'oz' => ['Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false], - 'cup' => ['Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false], - 'pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false], - 'us_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false], - 'uk_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false], - 'qt' => ['Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false], - 'gal' => ['Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false], - 'l' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true], - 'lt' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true], - ]; - - /** - * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM(). - * - * @var mixed[] - */ - private static $conversionMultipliers = [ - 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'], - 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'], - 'E' => ['multiplier' => 1E18, 'name' => 'exa'], - 'P' => ['multiplier' => 1E15, 'name' => 'peta'], - 'T' => ['multiplier' => 1E12, 'name' => 'tera'], - 'G' => ['multiplier' => 1E9, 'name' => 'giga'], - 'M' => ['multiplier' => 1E6, 'name' => 'mega'], - 'k' => ['multiplier' => 1E3, 'name' => 'kilo'], - 'h' => ['multiplier' => 1E2, 'name' => 'hecto'], - 'e' => ['multiplier' => 1E1, 'name' => 'deka'], - 'd' => ['multiplier' => 1E-1, 'name' => 'deci'], - 'c' => ['multiplier' => 1E-2, 'name' => 'centi'], - 'm' => ['multiplier' => 1E-3, 'name' => 'milli'], - 'u' => ['multiplier' => 1E-6, 'name' => 'micro'], - 'n' => ['multiplier' => 1E-9, 'name' => 'nano'], - 'p' => ['multiplier' => 1E-12, 'name' => 'pico'], - 'f' => ['multiplier' => 1E-15, 'name' => 'femto'], - 'a' => ['multiplier' => 1E-18, 'name' => 'atto'], - 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'], - 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'], - ]; - - /** - * Details of the Units of measure conversion factors, organised by group. - * - * @var mixed[] - */ - private static $unitConversions = [ - 'Mass' => [ - 'g' => [ - 'g' => 1.0, - 'sg' => 6.85220500053478E-05, - 'lbm' => 2.20462291469134E-03, - 'u' => 6.02217000000000E+23, - 'ozm' => 3.52739718003627E-02, - ], - 'sg' => [ - 'g' => 1.45938424189287E+04, - 'sg' => 1.0, - 'lbm' => 3.21739194101647E+01, - 'u' => 8.78866000000000E+27, - 'ozm' => 5.14782785944229E+02, - ], - 'lbm' => [ - 'g' => 4.5359230974881148E+02, - 'sg' => 3.10810749306493E-02, - 'lbm' => 1.0, - 'u' => 2.73161000000000E+26, - 'ozm' => 1.60000023429410E+01, - ], - 'u' => [ - 'g' => 1.66053100460465E-24, - 'sg' => 1.13782988532950E-28, - 'lbm' => 3.66084470330684E-27, - 'u' => 1.0, - 'ozm' => 5.85735238300524E-26, - ], - 'ozm' => [ - 'g' => 2.83495152079732E+01, - 'sg' => 1.94256689870811E-03, - 'lbm' => 6.24999908478882E-02, - 'u' => 1.70725600000000E+25, - 'ozm' => 1.0, - ], - ], - 'Distance' => [ - 'm' => [ - 'm' => 1.0, - 'mi' => 6.21371192237334E-04, - 'Nmi' => 5.39956803455724E-04, - 'in' => 3.93700787401575E+01, - 'ft' => 3.28083989501312E+00, - 'yd' => 1.09361329797891E+00, - 'ang' => 1.00000000000000E+10, - 'Pica' => 2.83464566929116E+03, - ], - 'mi' => [ - 'm' => 1.60934400000000E+03, - 'mi' => 1.0, - 'Nmi' => 8.68976241900648E-01, - 'in' => 6.33600000000000E+04, - 'ft' => 5.28000000000000E+03, - 'yd' => 1.76000000000000E+03, - 'ang' => 1.60934400000000E+13, - 'Pica' => 4.56191999999971E+06, - ], - 'Nmi' => [ - 'm' => 1.85200000000000E+03, - 'mi' => 1.15077944802354E+00, - 'Nmi' => 1.0, - 'in' => 7.29133858267717E+04, - 'ft' => 6.07611548556430E+03, - 'yd' => 2.02537182785694E+03, - 'ang' => 1.85200000000000E+13, - 'Pica' => 5.24976377952723E+06, - ], - 'in' => [ - 'm' => 2.54000000000000E-02, - 'mi' => 1.57828282828283E-05, - 'Nmi' => 1.37149028077754E-05, - 'in' => 1.0, - 'ft' => 8.33333333333333E-02, - 'yd' => 2.77777777686643E-02, - 'ang' => 2.54000000000000E+08, - 'Pica' => 7.19999999999955E+01, - ], - 'ft' => [ - 'm' => 3.04800000000000E-01, - 'mi' => 1.89393939393939E-04, - 'Nmi' => 1.64578833693305E-04, - 'in' => 1.20000000000000E+01, - 'ft' => 1.0, - 'yd' => 3.33333333223972E-01, - 'ang' => 3.04800000000000E+09, - 'Pica' => 8.63999999999946E+02, - ], - 'yd' => [ - 'm' => 9.14400000300000E-01, - 'mi' => 5.68181818368230E-04, - 'Nmi' => 4.93736501241901E-04, - 'in' => 3.60000000118110E+01, - 'ft' => 3.00000000000000E+00, - 'yd' => 1.0, - 'ang' => 9.14400000300000E+09, - 'Pica' => 2.59200000085023E+03, - ], - 'ang' => [ - 'm' => 1.00000000000000E-10, - 'mi' => 6.21371192237334E-14, - 'Nmi' => 5.39956803455724E-14, - 'in' => 3.93700787401575E-09, - 'ft' => 3.28083989501312E-10, - 'yd' => 1.09361329797891E-10, - 'ang' => 1.0, - 'Pica' => 2.83464566929116E-07, - ], - 'Pica' => [ - 'm' => 3.52777777777800E-04, - 'mi' => 2.19205948372629E-07, - 'Nmi' => 1.90484761219114E-07, - 'in' => 1.38888888888898E-02, - 'ft' => 1.15740740740748E-03, - 'yd' => 3.85802469009251E-04, - 'ang' => 3.52777777777800E+06, - 'Pica' => 1.0, - ], - ], - 'Time' => [ - 'yr' => [ - 'yr' => 1.0, - 'day' => 365.25, - 'hr' => 8766.0, - 'mn' => 525960.0, - 'sec' => 31557600.0, - ], - 'day' => [ - 'yr' => 2.73785078713210E-03, - 'day' => 1.0, - 'hr' => 24.0, - 'mn' => 1440.0, - 'sec' => 86400.0, - ], - 'hr' => [ - 'yr' => 1.14077116130504E-04, - 'day' => 4.16666666666667E-02, - 'hr' => 1.0, - 'mn' => 60.0, - 'sec' => 3600.0, - ], - 'mn' => [ - 'yr' => 1.90128526884174E-06, - 'day' => 6.94444444444444E-04, - 'hr' => 1.66666666666667E-02, - 'mn' => 1.0, - 'sec' => 60.0, - ], - 'sec' => [ - 'yr' => 3.16880878140289E-08, - 'day' => 1.15740740740741E-05, - 'hr' => 2.77777777777778E-04, - 'mn' => 1.66666666666667E-02, - 'sec' => 1.0, - ], - ], - 'Pressure' => [ - 'Pa' => [ - 'Pa' => 1.0, - 'p' => 1.0, - 'atm' => 9.86923299998193E-06, - 'at' => 9.86923299998193E-06, - 'mmHg' => 7.50061707998627E-03, - ], - 'p' => [ - 'Pa' => 1.0, - 'p' => 1.0, - 'atm' => 9.86923299998193E-06, - 'at' => 9.86923299998193E-06, - 'mmHg' => 7.50061707998627E-03, - ], - 'atm' => [ - 'Pa' => 1.01324996583000E+05, - 'p' => 1.01324996583000E+05, - 'atm' => 1.0, - 'at' => 1.0, - 'mmHg' => 760.0, - ], - 'at' => [ - 'Pa' => 1.01324996583000E+05, - 'p' => 1.01324996583000E+05, - 'atm' => 1.0, - 'at' => 1.0, - 'mmHg' => 760.0, - ], - 'mmHg' => [ - 'Pa' => 1.33322363925000E+02, - 'p' => 1.33322363925000E+02, - 'atm' => 1.31578947368421E-03, - 'at' => 1.31578947368421E-03, - 'mmHg' => 1.0, - ], - ], - 'Force' => [ - 'N' => [ - 'N' => 1.0, - 'dyn' => 1.0E+5, - 'dy' => 1.0E+5, - 'lbf' => 2.24808923655339E-01, - ], - 'dyn' => [ - 'N' => 1.0E-5, - 'dyn' => 1.0, - 'dy' => 1.0, - 'lbf' => 2.24808923655339E-06, - ], - 'dy' => [ - 'N' => 1.0E-5, - 'dyn' => 1.0, - 'dy' => 1.0, - 'lbf' => 2.24808923655339E-06, - ], - 'lbf' => [ - 'N' => 4.448222, - 'dyn' => 4.448222E+5, - 'dy' => 4.448222E+5, - 'lbf' => 1.0, - ], - ], - 'Energy' => [ - 'J' => [ - 'J' => 1.0, - 'e' => 9.99999519343231E+06, - 'c' => 2.39006249473467E-01, - 'cal' => 2.38846190642017E-01, - 'eV' => 6.24145700000000E+18, - 'ev' => 6.24145700000000E+18, - 'HPh' => 3.72506430801000E-07, - 'hh' => 3.72506430801000E-07, - 'Wh' => 2.77777916238711E-04, - 'wh' => 2.77777916238711E-04, - 'flb' => 2.37304222192651E+01, - 'BTU' => 9.47815067349015E-04, - 'btu' => 9.47815067349015E-04, - ], - 'e' => [ - 'J' => 1.00000048065700E-07, - 'e' => 1.0, - 'c' => 2.39006364353494E-08, - 'cal' => 2.38846305445111E-08, - 'eV' => 6.24146000000000E+11, - 'ev' => 6.24146000000000E+11, - 'HPh' => 3.72506609848824E-14, - 'hh' => 3.72506609848824E-14, - 'Wh' => 2.77778049754611E-11, - 'wh' => 2.77778049754611E-11, - 'flb' => 2.37304336254586E-06, - 'BTU' => 9.47815522922962E-11, - 'btu' => 9.47815522922962E-11, - ], - 'c' => [ - 'J' => 4.18399101363672E+00, - 'e' => 4.18398900257312E+07, - 'c' => 1.0, - 'cal' => 9.99330315287563E-01, - 'eV' => 2.61142000000000E+19, - 'ev' => 2.61142000000000E+19, - 'HPh' => 1.55856355899327E-06, - 'hh' => 1.55856355899327E-06, - 'Wh' => 1.16222030532950E-03, - 'wh' => 1.16222030532950E-03, - 'flb' => 9.92878733152102E+01, - 'BTU' => 3.96564972437776E-03, - 'btu' => 3.96564972437776E-03, - ], - 'cal' => [ - 'J' => 4.18679484613929E+00, - 'e' => 4.18679283372801E+07, - 'c' => 1.00067013349059E+00, - 'cal' => 1.0, - 'eV' => 2.61317000000000E+19, - 'ev' => 2.61317000000000E+19, - 'HPh' => 1.55960800463137E-06, - 'hh' => 1.55960800463137E-06, - 'Wh' => 1.16299914807955E-03, - 'wh' => 1.16299914807955E-03, - 'flb' => 9.93544094443283E+01, - 'BTU' => 3.96830723907002E-03, - 'btu' => 3.96830723907002E-03, - ], - 'eV' => [ - 'J' => 1.60219000146921E-19, - 'e' => 1.60218923136574E-12, - 'c' => 3.82933423195043E-20, - 'cal' => 3.82676978535648E-20, - 'eV' => 1.0, - 'ev' => 1.0, - 'HPh' => 5.96826078912344E-26, - 'hh' => 5.96826078912344E-26, - 'Wh' => 4.45053000026614E-23, - 'wh' => 4.45053000026614E-23, - 'flb' => 3.80206452103492E-18, - 'BTU' => 1.51857982414846E-22, - 'btu' => 1.51857982414846E-22, - ], - 'ev' => [ - 'J' => 1.60219000146921E-19, - 'e' => 1.60218923136574E-12, - 'c' => 3.82933423195043E-20, - 'cal' => 3.82676978535648E-20, - 'eV' => 1.0, - 'ev' => 1.0, - 'HPh' => 5.96826078912344E-26, - 'hh' => 5.96826078912344E-26, - 'Wh' => 4.45053000026614E-23, - 'wh' => 4.45053000026614E-23, - 'flb' => 3.80206452103492E-18, - 'BTU' => 1.51857982414846E-22, - 'btu' => 1.51857982414846E-22, - ], - 'HPh' => [ - 'J' => 2.68451741316170E+06, - 'e' => 2.68451612283024E+13, - 'c' => 6.41616438565991E+05, - 'cal' => 6.41186757845835E+05, - 'eV' => 1.67553000000000E+25, - 'ev' => 1.67553000000000E+25, - 'HPh' => 1.0, - 'hh' => 1.0, - 'Wh' => 7.45699653134593E+02, - 'wh' => 7.45699653134593E+02, - 'flb' => 6.37047316692964E+07, - 'BTU' => 2.54442605275546E+03, - 'btu' => 2.54442605275546E+03, - ], - 'hh' => [ - 'J' => 2.68451741316170E+06, - 'e' => 2.68451612283024E+13, - 'c' => 6.41616438565991E+05, - 'cal' => 6.41186757845835E+05, - 'eV' => 1.67553000000000E+25, - 'ev' => 1.67553000000000E+25, - 'HPh' => 1.0, - 'hh' => 1.0, - 'Wh' => 7.45699653134593E+02, - 'wh' => 7.45699653134593E+02, - 'flb' => 6.37047316692964E+07, - 'BTU' => 2.54442605275546E+03, - 'btu' => 2.54442605275546E+03, - ], - 'Wh' => [ - 'J' => 3.59999820554720E+03, - 'e' => 3.59999647518369E+10, - 'c' => 8.60422069219046E+02, - 'cal' => 8.59845857713046E+02, - 'eV' => 2.24692340000000E+22, - 'ev' => 2.24692340000000E+22, - 'HPh' => 1.34102248243839E-03, - 'hh' => 1.34102248243839E-03, - 'Wh' => 1.0, - 'wh' => 1.0, - 'flb' => 8.54294774062316E+04, - 'BTU' => 3.41213254164705E+00, - 'btu' => 3.41213254164705E+00, - ], - 'wh' => [ - 'J' => 3.59999820554720E+03, - 'e' => 3.59999647518369E+10, - 'c' => 8.60422069219046E+02, - 'cal' => 8.59845857713046E+02, - 'eV' => 2.24692340000000E+22, - 'ev' => 2.24692340000000E+22, - 'HPh' => 1.34102248243839E-03, - 'hh' => 1.34102248243839E-03, - 'Wh' => 1.0, - 'wh' => 1.0, - 'flb' => 8.54294774062316E+04, - 'BTU' => 3.41213254164705E+00, - 'btu' => 3.41213254164705E+00, - ], - 'flb' => [ - 'J' => 4.21400003236424E-02, - 'e' => 4.21399800687660E+05, - 'c' => 1.00717234301644E-02, - 'cal' => 1.00649785509554E-02, - 'eV' => 2.63015000000000E+17, - 'ev' => 2.63015000000000E+17, - 'HPh' => 1.56974211145130E-08, - 'hh' => 1.56974211145130E-08, - 'Wh' => 1.17055614802000E-05, - 'wh' => 1.17055614802000E-05, - 'flb' => 1.0, - 'BTU' => 3.99409272448406E-05, - 'btu' => 3.99409272448406E-05, - ], - 'BTU' => [ - 'J' => 1.05505813786749E+03, - 'e' => 1.05505763074665E+10, - 'c' => 2.52165488508168E+02, - 'cal' => 2.51996617135510E+02, - 'eV' => 6.58510000000000E+21, - 'ev' => 6.58510000000000E+21, - 'HPh' => 3.93015941224568E-04, - 'hh' => 3.93015941224568E-04, - 'Wh' => 2.93071851047526E-01, - 'wh' => 2.93071851047526E-01, - 'flb' => 2.50369750774671E+04, - 'BTU' => 1.0, - 'btu' => 1.0, - ], - 'btu' => [ - 'J' => 1.05505813786749E+03, - 'e' => 1.05505763074665E+10, - 'c' => 2.52165488508168E+02, - 'cal' => 2.51996617135510E+02, - 'eV' => 6.58510000000000E+21, - 'ev' => 6.58510000000000E+21, - 'HPh' => 3.93015941224568E-04, - 'hh' => 3.93015941224568E-04, - 'Wh' => 2.93071851047526E-01, - 'wh' => 2.93071851047526E-01, - 'flb' => 2.50369750774671E+04, - 'BTU' => 1.0, - 'btu' => 1.0, - ], - ], - 'Power' => [ - 'HP' => [ - 'HP' => 1.0, - 'h' => 1.0, - 'W' => 7.45701000000000E+02, - 'w' => 7.45701000000000E+02, - ], - 'h' => [ - 'HP' => 1.0, - 'h' => 1.0, - 'W' => 7.45701000000000E+02, - 'w' => 7.45701000000000E+02, - ], - 'W' => [ - 'HP' => 1.34102006031908E-03, - 'h' => 1.34102006031908E-03, - 'W' => 1.0, - 'w' => 1.0, - ], - 'w' => [ - 'HP' => 1.34102006031908E-03, - 'h' => 1.34102006031908E-03, - 'W' => 1.0, - 'w' => 1.0, - ], - ], - 'Magnetism' => [ - 'T' => [ - 'T' => 1.0, - 'ga' => 10000.0, - ], - 'ga' => [ - 'T' => 0.0001, - 'ga' => 1.0, - ], - ], - 'Liquid' => [ - 'tsp' => [ - 'tsp' => 1.0, - 'tbs' => 3.33333333333333E-01, - 'oz' => 1.66666666666667E-01, - 'cup' => 2.08333333333333E-02, - 'pt' => 1.04166666666667E-02, - 'us_pt' => 1.04166666666667E-02, - 'uk_pt' => 8.67558516821960E-03, - 'qt' => 5.20833333333333E-03, - 'gal' => 1.30208333333333E-03, - 'l' => 4.92999408400710E-03, - 'lt' => 4.92999408400710E-03, - ], - 'tbs' => [ - 'tsp' => 3.00000000000000E+00, - 'tbs' => 1.0, - 'oz' => 5.00000000000000E-01, - 'cup' => 6.25000000000000E-02, - 'pt' => 3.12500000000000E-02, - 'us_pt' => 3.12500000000000E-02, - 'uk_pt' => 2.60267555046588E-02, - 'qt' => 1.56250000000000E-02, - 'gal' => 3.90625000000000E-03, - 'l' => 1.47899822520213E-02, - 'lt' => 1.47899822520213E-02, - ], - 'oz' => [ - 'tsp' => 6.00000000000000E+00, - 'tbs' => 2.00000000000000E+00, - 'oz' => 1.0, - 'cup' => 1.25000000000000E-01, - 'pt' => 6.25000000000000E-02, - 'us_pt' => 6.25000000000000E-02, - 'uk_pt' => 5.20535110093176E-02, - 'qt' => 3.12500000000000E-02, - 'gal' => 7.81250000000000E-03, - 'l' => 2.95799645040426E-02, - 'lt' => 2.95799645040426E-02, - ], - 'cup' => [ - 'tsp' => 4.80000000000000E+01, - 'tbs' => 1.60000000000000E+01, - 'oz' => 8.00000000000000E+00, - 'cup' => 1.0, - 'pt' => 5.00000000000000E-01, - 'us_pt' => 5.00000000000000E-01, - 'uk_pt' => 4.16428088074541E-01, - 'qt' => 2.50000000000000E-01, - 'gal' => 6.25000000000000E-02, - 'l' => 2.36639716032341E-01, - 'lt' => 2.36639716032341E-01, - ], - 'pt' => [ - 'tsp' => 9.60000000000000E+01, - 'tbs' => 3.20000000000000E+01, - 'oz' => 1.60000000000000E+01, - 'cup' => 2.00000000000000E+00, - 'pt' => 1.0, - 'us_pt' => 1.0, - 'uk_pt' => 8.32856176149081E-01, - 'qt' => 5.00000000000000E-01, - 'gal' => 1.25000000000000E-01, - 'l' => 4.73279432064682E-01, - 'lt' => 4.73279432064682E-01, - ], - 'us_pt' => [ - 'tsp' => 9.60000000000000E+01, - 'tbs' => 3.20000000000000E+01, - 'oz' => 1.60000000000000E+01, - 'cup' => 2.00000000000000E+00, - 'pt' => 1.0, - 'us_pt' => 1.0, - 'uk_pt' => 8.32856176149081E-01, - 'qt' => 5.00000000000000E-01, - 'gal' => 1.25000000000000E-01, - 'l' => 4.73279432064682E-01, - 'lt' => 4.73279432064682E-01, - ], - 'uk_pt' => [ - 'tsp' => 1.15266000000000E+02, - 'tbs' => 3.84220000000000E+01, - 'oz' => 1.92110000000000E+01, - 'cup' => 2.40137500000000E+00, - 'pt' => 1.20068750000000E+00, - 'us_pt' => 1.20068750000000E+00, - 'uk_pt' => 1.0, - 'qt' => 6.00343750000000E-01, - 'gal' => 1.50085937500000E-01, - 'l' => 5.68260698087162E-01, - 'lt' => 5.68260698087162E-01, - ], - 'qt' => [ - 'tsp' => 1.92000000000000E+02, - 'tbs' => 6.40000000000000E+01, - 'oz' => 3.20000000000000E+01, - 'cup' => 4.00000000000000E+00, - 'pt' => 2.00000000000000E+00, - 'us_pt' => 2.00000000000000E+00, - 'uk_pt' => 1.66571235229816E+00, - 'qt' => 1.0, - 'gal' => 2.50000000000000E-01, - 'l' => 9.46558864129363E-01, - 'lt' => 9.46558864129363E-01, - ], - 'gal' => [ - 'tsp' => 7.68000000000000E+02, - 'tbs' => 2.56000000000000E+02, - 'oz' => 1.28000000000000E+02, - 'cup' => 1.60000000000000E+01, - 'pt' => 8.00000000000000E+00, - 'us_pt' => 8.00000000000000E+00, - 'uk_pt' => 6.66284940919265E+00, - 'qt' => 4.00000000000000E+00, - 'gal' => 1.0, - 'l' => 3.78623545651745E+00, - 'lt' => 3.78623545651745E+00, - ], - 'l' => [ - 'tsp' => 2.02840000000000E+02, - 'tbs' => 6.76133333333333E+01, - 'oz' => 3.38066666666667E+01, - 'cup' => 4.22583333333333E+00, - 'pt' => 2.11291666666667E+00, - 'us_pt' => 2.11291666666667E+00, - 'uk_pt' => 1.75975569552166E+00, - 'qt' => 1.05645833333333E+00, - 'gal' => 2.64114583333333E-01, - 'l' => 1.0, - 'lt' => 1.0, - ], - 'lt' => [ - 'tsp' => 2.02840000000000E+02, - 'tbs' => 6.76133333333333E+01, - 'oz' => 3.38066666666667E+01, - 'cup' => 4.22583333333333E+00, - 'pt' => 2.11291666666667E+00, - 'us_pt' => 2.11291666666667E+00, - 'uk_pt' => 1.75975569552166E+00, - 'qt' => 1.05645833333333E+00, - 'gal' => 2.64114583333333E-01, - 'l' => 1.0, - 'lt' => 1.0, - ], - ], - ]; - /** * parseComplex. * @@ -776,8 +73,6 @@ class Engineering * Excel Function: * BESSELI(x,ord) * - * @category Engineering Functions - * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELI returns the #VALUE! error value. * @param int $ord The order of the Bessel function. @@ -785,7 +80,7 @@ class Engineering * If $ord is nonnumeric, BESSELI returns the #VALUE! error value. * If $ord < 0, BESSELI returns the #NUM! error value. * - * @return float + * @return float|string Result, or a string containing an error */ public static function BESSELI($x, $ord) { @@ -799,7 +94,7 @@ class Engineering } if (abs($x) <= 30) { - $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord); + $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); $ordK = 1; $fSqrX = ($x * $x) / 4; do { @@ -831,15 +126,13 @@ class Engineering * Excel Function: * BESSELJ(x,ord) * - * @category Engineering Functions - * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELJ returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value. * If $ord < 0, BESSELJ returns the #NUM! error value. * - * @return float + * @return float|string Result, or a string containing an error */ public static function BESSELJ($x, $ord) { @@ -854,7 +147,7 @@ class Engineering $fResult = 0; if (abs($x) <= 30) { - $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord); + $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); $ordK = 1; $fSqrX = ($x * $x) / -4; do { @@ -924,15 +217,13 @@ class Engineering * Excel Function: * BESSELK(x,ord) * - * @category Engineering Functions - * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELK returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * If $ord is nonnumeric, BESSELK returns the #VALUE! error value. * If $ord < 0, BESSELK returns the #NUM! error value. * - * @return float + * @return float|string Result, or a string containing an error */ public static function BESSELK($x, $ord) { @@ -1013,15 +304,13 @@ class Engineering * Excel Function: * BESSELY(x,ord) * - * @category Engineering Functions - * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELK returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. * If $ord is nonnumeric, BESSELK returns the #VALUE! error value. * If $ord < 0, BESSELK returns the #NUM! error value. * - * @return float + * @return float|string Result, or a string containing an error */ public static function BESSELY($x, $ord) { @@ -1067,8 +356,6 @@ class Engineering * Excel Function: * BIN2DEC(x) * - * @category Engineering Functions - * * @param string $x The binary number (as a string) that you want to convert. The number * cannot contain more than 10 characters (10 bits). The most significant * bit of number is the sign bit. The remaining 9 bits are magnitude bits. @@ -1116,8 +403,6 @@ class Engineering * Excel Function: * BIN2HEX(x[,places]) * - * @category Engineering Functions - * * @param string $x The binary number (as a string) that you want to convert. The number * cannot contain more than 10 characters (10 bits). The most significant * bit of number is the sign bit. The remaining 9 bits are magnitude bits. @@ -1172,8 +457,6 @@ class Engineering * Excel Function: * BIN2OCT(x[,places]) * - * @category Engineering Functions - * * @param string $x The binary number (as a string) that you want to convert. The number * cannot contain more than 10 characters (10 bits). The most significant * bit of number is the sign bit. The remaining 9 bits are magnitude bits. @@ -1227,8 +510,6 @@ class Engineering * Excel Function: * DEC2BIN(x[,places]) * - * @category Engineering Functions - * * @param string $x The decimal integer you want to convert. If number is negative, * valid place values are ignored and DEC2BIN returns a 10-character * (10-bit) binary number in which the most significant bit is the sign @@ -1288,8 +569,6 @@ class Engineering * Excel Function: * DEC2HEX(x[,places]) * - * @category Engineering Functions - * * @param string $x The decimal integer you want to convert. If number is negative, * places is ignored and DEC2HEX returns a 10-character (40-bit) * hexadecimal number in which the most significant bit is the sign @@ -1343,8 +622,6 @@ class Engineering * Excel Function: * DEC2OCT(x[,places]) * - * @category Engineering Functions - * * @param string $x The decimal integer you want to convert. If number is negative, * places is ignored and DEC2OCT returns a 10-character (30-bit) * octal number in which the most significant bit is the sign bit. @@ -1399,8 +676,6 @@ class Engineering * Excel Function: * HEX2BIN(x[,places]) * - * @category Engineering Functions - * * @param string $x the hexadecimal number you want to convert. * Number cannot contain more than 10 characters. * The most significant bit of number is the sign bit (40th bit from the right). @@ -1444,8 +719,6 @@ class Engineering * Excel Function: * HEX2DEC(x) * - * @category Engineering Functions - * * @param string $x The hexadecimal number you want to convert. This number cannot * contain more than 10 characters (40 bits). The most significant * bit of number is the sign bit. The remaining 39 bits are magnitude @@ -1495,8 +768,6 @@ class Engineering * Excel Function: * HEX2OCT(x[,places]) * - * @category Engineering Functions - * * @param string $x The hexadecimal number you want to convert. Number cannot * contain more than 10 characters. The most significant bit of * number is the sign bit. The remaining 39 bits are magnitude @@ -1549,8 +820,6 @@ class Engineering * Excel Function: * OCT2BIN(x[,places]) * - * @category Engineering Functions - * * @param string $x The octal number you want to convert. Number may not * contain more than 10 characters. The most significant * bit of number is the sign bit. The remaining 29 bits @@ -1600,8 +869,6 @@ class Engineering * Excel Function: * OCT2DEC(x) * - * @category Engineering Functions - * * @param string $x The octal number you want to convert. Number may not contain * more than 10 octal characters (30 bits). The most significant * bit of number is the sign bit. The remaining 29 bits are @@ -1646,8 +913,6 @@ class Engineering * Excel Function: * OCT2HEX(x[,places]) * - * @category Engineering Functions - * * @param string $x The octal number you want to convert. Number may not contain * more than 10 octal characters (30 bits). The most significant * bit of number is the sign bit. The remaining 29 bits are @@ -1693,8 +958,6 @@ class Engineering * Excel Function: * COMPLEX(realNumber,imaginary[,suffix]) * - * @category Engineering Functions - * * @param float $realNumber the real coefficient of the complex number * @param float $imaginary the imaginary coefficient of the complex number * @param string $suffix The suffix for the imaginary component of the complex number. @@ -1708,7 +971,8 @@ class Engineering $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary); $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix); - if (((is_numeric($realNumber)) && (is_numeric($imaginary))) && + if ( + ((is_numeric($realNumber)) && (is_numeric($imaginary))) && (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) ) { $complex = new Complex($realNumber, $imaginary, $suffix); @@ -1727,8 +991,6 @@ class Engineering * Excel Function: * IMAGINARY(complexNumber) * - * @category Engineering Functions - * * @param string $complexNumber the complex number for which you want the imaginary * coefficient * @@ -1749,8 +1011,6 @@ class Engineering * Excel Function: * IMREAL(complexNumber) * - * @category Engineering Functions - * * @param string $complexNumber the complex number for which you want the real coefficient * * @return float @@ -2346,8 +1606,6 @@ class Engineering * * @param mixed $value * - * @throws Exception - * * @return int */ private static function validateBitwiseArgument($value) @@ -2359,7 +1617,7 @@ class Engineering } elseif (is_numeric($value)) { if ($value == (int) ($value)) { $value = (int) ($value); - if (($value > pow(2, 48) - 1) || ($value < 0)) { + if (($value > 2 ** 48 - 1) || ($value < 0)) { throw new Exception(Functions::NAN()); } @@ -2380,8 +1638,6 @@ class Engineering * Excel Function: * BITAND(number1, number2) * - * @category Engineering Functions - * * @param int $number1 * @param int $number2 * @@ -2407,8 +1663,6 @@ class Engineering * Excel Function: * BITOR(number1, number2) * - * @category Engineering Functions - * * @param int $number1 * @param int $number2 * @@ -2434,8 +1688,6 @@ class Engineering * Excel Function: * BITXOR(number1, number2) * - * @category Engineering Functions - * * @param int $number1 * @param int $number2 * @@ -2461,8 +1713,6 @@ class Engineering * Excel Function: * BITLSHIFT(number, shift_amount) * - * @category Engineering Functions - * * @param int $number * @param int $shiftAmount * @@ -2479,7 +1729,7 @@ class Engineering $shiftAmount = Functions::flattenSingleValue($shiftAmount); $result = $number << $shiftAmount; - if ($result > pow(2, 48) - 1) { + if ($result > 2 ** 48 - 1) { return Functions::NAN(); } @@ -2494,8 +1744,6 @@ class Engineering * Excel Function: * BITRSHIFT(number, shift_amount) * - * @category Engineering Functions - * * @param int $number * @param int $shiftAmount * @@ -2634,69 +1882,68 @@ class Engineering * getConversionGroups * Returns a list of the different conversion groups for UOM conversions. * + * @Deprecated Use the getConversionCategories() method in the ConvertUOM class instead + * * @return array */ public static function getConversionGroups() { - $conversionGroups = []; - foreach (self::$conversionUnits as $conversionUnit) { - $conversionGroups[] = $conversionUnit['Group']; - } - - return array_merge(array_unique($conversionGroups)); + return Engineering\ConvertUOM::getConversionCategories(); } /** * getConversionGroupUnits * Returns an array of units of measure, for a specified conversion group, or for all groups. * - * @param string $group The group whose units of measure you want to retrieve + * @Deprecated Use the getConversionCategoryUnits() method in the ConvertUOM class instead + * + * @param null|mixed $category * * @return array */ - public static function getConversionGroupUnits($group = null) + public static function getConversionGroupUnits($category = null) { - $conversionGroups = []; - foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) { - if (($group === null) || ($conversionGroup['Group'] == $group)) { - $conversionGroups[$conversionGroup['Group']][] = $conversionUnit; - } - } - - return $conversionGroups; + return Engineering\ConvertUOM::getConversionCategoryUnits($category); } /** * getConversionGroupUnitDetails. * - * @param string $group The group whose units of measure you want to retrieve + * @Deprecated Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead + * + * @param null|mixed $category * * @return array */ - public static function getConversionGroupUnitDetails($group = null) + public static function getConversionGroupUnitDetails($category = null) { - $conversionGroups = []; - foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) { - if (($group === null) || ($conversionGroup['Group'] == $group)) { - $conversionGroups[$conversionGroup['Group']][] = [ - 'unit' => $conversionUnit, - 'description' => $conversionGroup['Unit Name'], - ]; - } - } - - return $conversionGroups; + return Engineering\ConvertUOM::getConversionCategoryUnitDetails($category); } /** * getConversionMultipliers * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM(). * + * @Deprecated Use the getConversionMultipliers() method in the ConvertUOM class instead + * * @return array of mixed */ public static function getConversionMultipliers() { - return self::$conversionMultipliers; + return Engineering\ConvertUOM::getConversionMultipliers(); + } + + /** + * getBinaryConversionMultipliers + * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM(). + * + * @Deprecated Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead + * + * @return array of mixed + */ + public static function getBinaryConversionMultipliers() + { + return Engineering\ConvertUOM::getBinaryConversionMultipliers(); } /** @@ -2709,7 +1956,9 @@ class Engineering * Excel Function: * CONVERT(value,fromUOM,toUOM) * - * @param float $value the value in fromUOM to convert + * @Deprecated Use the CONVERT() method in the ConvertUOM class instead + * + * @param float|int $value the value in fromUOM to convert * @param string $fromUOM the units for value * @param string $toUOM the units for the result * @@ -2717,91 +1966,6 @@ class Engineering */ public static function CONVERTUOM($value, $fromUOM, $toUOM) { - $value = Functions::flattenSingleValue($value); - $fromUOM = Functions::flattenSingleValue($fromUOM); - $toUOM = Functions::flattenSingleValue($toUOM); - - if (!is_numeric($value)) { - return Functions::VALUE(); - } - $fromMultiplier = 1.0; - if (isset(self::$conversionUnits[$fromUOM])) { - $unitGroup1 = self::$conversionUnits[$fromUOM]['Group']; - } else { - $fromMultiplier = substr($fromUOM, 0, 1); - $fromUOM = substr($fromUOM, 1); - if (isset(self::$conversionMultipliers[$fromMultiplier])) { - $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier']; - } else { - return Functions::NA(); - } - if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) { - $unitGroup1 = self::$conversionUnits[$fromUOM]['Group']; - } else { - return Functions::NA(); - } - } - $value *= $fromMultiplier; - - $toMultiplier = 1.0; - if (isset(self::$conversionUnits[$toUOM])) { - $unitGroup2 = self::$conversionUnits[$toUOM]['Group']; - } else { - $toMultiplier = substr($toUOM, 0, 1); - $toUOM = substr($toUOM, 1); - if (isset(self::$conversionMultipliers[$toMultiplier])) { - $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier']; - } else { - return Functions::NA(); - } - if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) { - $unitGroup2 = self::$conversionUnits[$toUOM]['Group']; - } else { - return Functions::NA(); - } - } - if ($unitGroup1 != $unitGroup2) { - return Functions::NA(); - } - - if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) { - // We've already factored $fromMultiplier into the value, so we need - // to reverse it again - return $value / $fromMultiplier; - } elseif ($unitGroup1 == 'Temperature') { - if (($fromUOM == 'F') || ($fromUOM == 'fah')) { - if (($toUOM == 'F') || ($toUOM == 'fah')) { - return $value; - } - $value = (($value - 32) / 1.8); - if (($toUOM == 'K') || ($toUOM == 'kel')) { - $value += 273.15; - } - - return $value; - } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) && - (($toUOM == 'K') || ($toUOM == 'kel')) - ) { - return $value; - } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) && - (($toUOM == 'C') || ($toUOM == 'cel')) - ) { - return $value; - } - if (($toUOM == 'F') || ($toUOM == 'fah')) { - if (($fromUOM == 'K') || ($fromUOM == 'kel')) { - $value -= 273.15; - } - - return ($value * 1.8) + 32; - } - if (($toUOM == 'C') || ($toUOM == 'cel')) { - return $value - 273.15; - } - - return $value + 273.15; - } - - return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier; + return Engineering\ConvertUOM::CONVERT($value, $fromUOM, $toUOM); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php new file mode 100644 index 00000000000..0aafe05ec3a --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php @@ -0,0 +1,684 @@ + ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true], + 'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false], + 'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false], + 'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true], + 'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false], + 'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false], + 'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false], + 'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false], + 'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false], + 'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false], + 'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false], + 'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false], + 'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false], + 'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false], + 'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false], + 'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false], + // Distance + 'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true], + 'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false], + 'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false], + 'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false], + 'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false], + 'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false], + 'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true], + 'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false], + 'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false], + 'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false], + 'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false], + 'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false], + 'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false], + 'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false], + 'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false], + // Time + 'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false], + 'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false], + 'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false], + 'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false], + 'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false], + 'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false], + 'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true], + 's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true], + // Pressure + 'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true], + 'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true], + 'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true], + 'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true], + 'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true], + 'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true], + 'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true], + // Force + 'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true], + 'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true], + 'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true], + 'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false], + 'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true], + // Energy + 'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true], + 'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true], + 'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true], + 'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true], + 'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true], + 'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true], + 'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false], + 'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false], + 'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true], + 'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true], + 'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false], + 'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false], + 'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false], + // Power + 'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false], + 'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false], + 'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true], + 'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true], + 'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false], + 'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true], + 'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true], + // Temperature + 'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false], + 'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false], + 'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false], + 'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false], + 'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false], + 'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false], + 'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false], + 'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false], + // Volume + 'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true], + 'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true], + 'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true], + 'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false], + 'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false], + 'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false], + 'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false], + 'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false], + 'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false], + 'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false], + 'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false], + 'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false], + 'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false], + 'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false], + 'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false], + 'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true], + 'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true], + 'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false], + 'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false], + 'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false], + 'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false], + 'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false], + 'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false], + 'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false], + 'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false], + 'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true], + 'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true], + 'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false], + 'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false], + 'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false], + 'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false], + 'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false], + 'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false], + 'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false], + 'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false], + 'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false], + 'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false], + 'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false], + 'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false], + 'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false], + // Area + 'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true], + 'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false], + 'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false], + 'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true], + 'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true], + 'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true], + 'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false], + 'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false], + 'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false], + 'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false], + 'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false], + 'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false], + 'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true], + 'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true], + 'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false], + 'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false], + 'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false], + 'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false], + 'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false], + 'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false], + 'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false], + 'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false], + 'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false], + 'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false], + 'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false], + // Information + 'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true], + 'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true], + // Speed + 'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true], + 'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true], + 'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true], + 'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true], + 'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false], + 'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false], + 'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false], + ]; + + /** + * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM(). + * + * @var mixed[] + */ + private static $conversionMultipliers = [ + 'Y' => ['multiplier' => 1E24, 'name' => 'yotta'], + 'Z' => ['multiplier' => 1E21, 'name' => 'zetta'], + 'E' => ['multiplier' => 1E18, 'name' => 'exa'], + 'P' => ['multiplier' => 1E15, 'name' => 'peta'], + 'T' => ['multiplier' => 1E12, 'name' => 'tera'], + 'G' => ['multiplier' => 1E9, 'name' => 'giga'], + 'M' => ['multiplier' => 1E6, 'name' => 'mega'], + 'k' => ['multiplier' => 1E3, 'name' => 'kilo'], + 'h' => ['multiplier' => 1E2, 'name' => 'hecto'], + 'e' => ['multiplier' => 1E1, 'name' => 'dekao'], + 'da' => ['multiplier' => 1E1, 'name' => 'dekao'], + 'd' => ['multiplier' => 1E-1, 'name' => 'deci'], + 'c' => ['multiplier' => 1E-2, 'name' => 'centi'], + 'm' => ['multiplier' => 1E-3, 'name' => 'milli'], + 'u' => ['multiplier' => 1E-6, 'name' => 'micro'], + 'n' => ['multiplier' => 1E-9, 'name' => 'nano'], + 'p' => ['multiplier' => 1E-12, 'name' => 'pico'], + 'f' => ['multiplier' => 1E-15, 'name' => 'femto'], + 'a' => ['multiplier' => 1E-18, 'name' => 'atto'], + 'z' => ['multiplier' => 1E-21, 'name' => 'zepto'], + 'y' => ['multiplier' => 1E-24, 'name' => 'yocto'], + ]; + + /** + * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM(). + * + * @var mixed[] + */ + private static $binaryConversionMultipliers = [ + 'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'], + 'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'], + 'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'], + 'Pi' => ['multiplier' => 2 ** 50, 'name' => 'pebi'], + 'Ti' => ['multiplier' => 2 ** 40, 'name' => 'tebi'], + 'Gi' => ['multiplier' => 2 ** 30, 'name' => 'gibi'], + 'Mi' => ['multiplier' => 2 ** 20, 'name' => 'mebi'], + 'ki' => ['multiplier' => 2 ** 10, 'name' => 'kibi'], + ]; + + /** + * Details of the Units of measure conversion factors, organised by group. + * + * @var mixed[] + */ + private static $unitConversions = [ + // Conversion uses gram (g) as an intermediate unit + self::CATEGORY_WEIGHT_AND_MASS => [ + 'g' => 1.0, + 'sg' => 6.85217658567918E-05, + 'lbm' => 2.20462262184878E-03, + 'u' => 6.02214179421676E+23, + 'ozm' => 3.52739619495804E-02, + 'grain' => 1.54323583529414E+01, + 'cwt' => 2.20462262184878E-05, + 'shweight' => 2.20462262184878E-05, + 'uk_cwt' => 1.96841305522212E-05, + 'lcwt' => 1.96841305522212E-05, + 'hweight' => 1.96841305522212E-05, + 'stone' => 1.57473044417770E-04, + 'ton' => 1.10231131092439E-06, + 'uk_ton' => 9.84206527611061E-07, + 'LTON' => 9.84206527611061E-07, + 'brton' => 9.84206527611061E-07, + ], + // Conversion uses meter (m) as an intermediate unit + self::CATEGORY_DISTANCE => [ + 'm' => 1.0, + 'mi' => 6.21371192237334E-04, + 'Nmi' => 5.39956803455724E-04, + 'in' => 3.93700787401575E+01, + 'ft' => 3.28083989501312E+00, + 'yd' => 1.09361329833771E+00, + 'ang' => 1.0E+10, + 'ell' => 8.74890638670166E-01, + 'ly' => 1.05700083402462E-16, + 'parsec' => 3.24077928966473E-17, + 'pc' => 3.24077928966473E-17, + 'Pica' => 2.83464566929134E+03, + 'Picapt' => 2.83464566929134E+03, + 'pica' => 2.36220472440945E+02, + 'survey_mi' => 6.21369949494950E-04, + ], + // Conversion uses second (s) as an intermediate unit + self::CATEGORY_TIME => [ + 'yr' => 3.16880878140289E-08, + 'day' => 1.15740740740741E-05, + 'd' => 1.15740740740741E-05, + 'hr' => 2.77777777777778E-04, + 'mn' => 1.66666666666667E-02, + 'min' => 1.66666666666667E-02, + 'sec' => 1.0, + 's' => 1.0, + ], + // Conversion uses Pascal (Pa) as an intermediate unit + self::CATEGORY_PRESSURE => [ + 'Pa' => 1.0, + 'p' => 1.0, + 'atm' => 9.86923266716013E-06, + 'at' => 9.86923266716013E-06, + 'mmHg' => 7.50063755419211E-03, + 'psi' => 1.45037737730209E-04, + 'Torr' => 7.50061682704170E-03, + ], + // Conversion uses Newton (N) as an intermediate unit + self::CATEGORY_FORCE => [ + 'N' => 1.0, + 'dyn' => 1.0E+5, + 'dy' => 1.0E+5, + 'lbf' => 2.24808923655339E-01, + 'pond' => 1.01971621297793E+02, + ], + // Conversion uses Joule (J) as an intermediate unit + self::CATEGORY_ENERGY => [ + 'J' => 1.0, + 'e' => 9.99999519343231E+06, + 'c' => 2.39006249473467E-01, + 'cal' => 2.38846190642017E-01, + 'eV' => 6.24145700000000E+18, + 'ev' => 6.24145700000000E+18, + 'HPh' => 3.72506430801000E-07, + 'hh' => 3.72506430801000E-07, + 'Wh' => 2.77777916238711E-04, + 'wh' => 2.77777916238711E-04, + 'flb' => 2.37304222192651E+01, + 'BTU' => 9.47815067349015E-04, + 'btu' => 9.47815067349015E-04, + ], + // Conversion uses Horsepower (HP) as an intermediate unit + self::CATEGORY_POWER => [ + 'HP' => 1.0, + 'h' => 1.0, + 'W' => 7.45699871582270E+02, + 'w' => 7.45699871582270E+02, + 'PS' => 1.01386966542400E+00, + ], + // Conversion uses Tesla (T) as an intermediate unit + self::CATEGORY_MAGNETISM => [ + 'T' => 1.0, + 'ga' => 10000.0, + ], + // Conversion uses litre (l) as an intermediate unit + self::CATEGORY_VOLUME => [ + 'l' => 1.0, + 'L' => 1.0, + 'lt' => 1.0, + 'tsp' => 2.02884136211058E+02, + 'tspm' => 2.0E+02, + 'tbs' => 6.76280454036860E+01, + 'oz' => 3.38140227018430E+01, + 'cup' => 4.22675283773038E+00, + 'pt' => 2.11337641886519E+00, + 'us_pt' => 2.11337641886519E+00, + 'uk_pt' => 1.75975398639270E+00, + 'qt' => 1.05668820943259E+00, + 'uk_qt' => 8.79876993196351E-01, + 'gal' => 2.64172052358148E-01, + 'uk_gal' => 2.19969248299088E-01, + 'ang3' => 1.0E+27, + 'ang^3' => 1.0E+27, + 'barrel' => 6.28981077043211E-03, + 'bushel' => 2.83775932584017E-02, + 'in3' => 6.10237440947323E+01, + 'in^3' => 6.10237440947323E+01, + 'ft3' => 3.53146667214886E-02, + 'ft^3' => 3.53146667214886E-02, + 'ly3' => 1.18093498844171E-51, + 'ly^3' => 1.18093498844171E-51, + 'm3' => 1.0E-03, + 'm^3' => 1.0E-03, + 'mi3' => 2.39912758578928E-13, + 'mi^3' => 2.39912758578928E-13, + 'yd3' => 1.30795061931439E-03, + 'yd^3' => 1.30795061931439E-03, + 'Nmi3' => 1.57426214685811E-13, + 'Nmi^3' => 1.57426214685811E-13, + 'Pica3' => 2.27769904358706E+07, + 'Pica^3' => 2.27769904358706E+07, + 'Picapt3' => 2.27769904358706E+07, + 'Picapt^3' => 2.27769904358706E+07, + 'GRT' => 3.53146667214886E-04, + 'regton' => 3.53146667214886E-04, + 'MTON' => 8.82866668037215E-04, + ], + // Conversion uses hectare (ha) as an intermediate unit + self::CATEGORY_AREA => [ + 'ha' => 1.0, + 'uk_acre' => 2.47105381467165E+00, + 'us_acre' => 2.47104393046628E+00, + 'ang2' => 1.0E+24, + 'ang^2' => 1.0E+24, + 'ar' => 1.0E+02, + 'ft2' => 1.07639104167097E+05, + 'ft^2' => 1.07639104167097E+05, + 'in2' => 1.55000310000620E+07, + 'in^2' => 1.55000310000620E+07, + 'ly2' => 1.11725076312873E-28, + 'ly^2' => 1.11725076312873E-28, + 'm2' => 1.0E+04, + 'm^2' => 1.0E+04, + 'Morgen' => 4.0E+00, + 'mi2' => 3.86102158542446E-03, + 'mi^2' => 3.86102158542446E-03, + 'Nmi2' => 2.91553349598123E-03, + 'Nmi^2' => 2.91553349598123E-03, + 'Pica2' => 8.03521607043214E+10, + 'Pica^2' => 8.03521607043214E+10, + 'Picapt2' => 8.03521607043214E+10, + 'Picapt^2' => 8.03521607043214E+10, + 'yd2' => 1.19599004630108E+04, + 'yd^2' => 1.19599004630108E+04, + ], + // Conversion uses bit (bit) as an intermediate unit + self::CATEGORY_INFORMATION => [ + 'bit' => 1.0, + 'byte' => 0.125, + ], + // Conversion uses Meters per Second (m/s) as an intermediate unit + self::CATEGORY_SPEED => [ + 'm/s' => 1.0, + 'm/sec' => 1.0, + 'm/h' => 3.60E+03, + 'm/hr' => 3.60E+03, + 'mph' => 2.23693629205440E+00, + 'admkn' => 1.94260256941567E+00, + 'kn' => 1.94384449244060E+00, + ], + ]; + + /** + * getConversionGroups + * Returns a list of the different conversion groups for UOM conversions. + * + * @return array + */ + public static function getConversionCategories() + { + $conversionGroups = []; + foreach (self::$conversionUnits as $conversionUnit) { + $conversionGroups[] = $conversionUnit['Group']; + } + + return array_merge(array_unique($conversionGroups)); + } + + /** + * getConversionGroupUnits + * Returns an array of units of measure, for a specified conversion group, or for all groups. + * + * @param string $category The group whose units of measure you want to retrieve + * + * @return array + */ + public static function getConversionCategoryUnits($category = null) + { + $conversionGroups = []; + foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) { + if (($category === null) || ($conversionGroup['Group'] == $category)) { + $conversionGroups[$conversionGroup['Group']][] = $conversionUnit; + } + } + + return $conversionGroups; + } + + /** + * getConversionGroupUnitDetails. + * + * @param string $category The group whose units of measure you want to retrieve + * + * @return array + */ + public static function getConversionCategoryUnitDetails($category = null) + { + $conversionGroups = []; + foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) { + if (($category === null) || ($conversionGroup['Group'] == $category)) { + $conversionGroups[$conversionGroup['Group']][] = [ + 'unit' => $conversionUnit, + 'description' => $conversionGroup['Unit Name'], + ]; + } + } + + return $conversionGroups; + } + + /** + * getConversionMultipliers + * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM(). + * + * @return array of mixed + */ + public static function getConversionMultipliers() + { + return self::$conversionMultipliers; + } + + /** + * getBinaryConversionMultipliers + * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM(). + * + * @return array of mixed + */ + public static function getBinaryConversionMultipliers() + { + return self::$binaryConversionMultipliers; + } + + /** + * CONVERT. + * + * Converts a number from one measurement system to another. + * For example, CONVERT can translate a table of distances in miles to a table of distances + * in kilometers. + * + * Excel Function: + * CONVERT(value,fromUOM,toUOM) + * + * @param float|int $value the value in fromUOM to convert + * @param string $fromUOM the units for value + * @param string $toUOM the units for the result + * + * @return float|string + */ + public static function CONVERT($value, $fromUOM, $toUOM) + { + $value = Functions::flattenSingleValue($value); + $fromUOM = Functions::flattenSingleValue($fromUOM); + $toUOM = Functions::flattenSingleValue($toUOM); + + if (!is_numeric($value)) { + return Functions::VALUE(); + } + + try { + [$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM); + [$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM); + } catch (Exception $e) { + return Functions::NA(); + } + + if ($fromCategory !== $toCategory) { + return Functions::NA(); + } + + $value *= $fromMultiplier; + + if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) { + // We've already factored $fromMultiplier into the value, so we need + // to reverse it again + return $value / $fromMultiplier; + } elseif ($fromUOM === $toUOM) { + return $value / $toMultiplier; + } elseif ($fromCategory === self::CATEGORY_TEMPERATURE) { + return self::convertTemperature($fromUOM, $toUOM, $value); + } + + $baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]); + + return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier; + } + + private static function getUOMDetails(string $uom) + { + if (isset(self::$conversionUnits[$uom])) { + $unitCategory = self::$conversionUnits[$uom]['Group']; + + return [$uom, $unitCategory, 1.0]; + } + + // Check 1-character standard metric multiplier prefixes + $multiplierType = substr($uom, 0, 1); + $uom = substr($uom, 1); + if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) { + if (self::$conversionUnits[$uom]['AllowPrefix'] === false) { + throw new Exception('Prefix not allowed for UoM'); + } + $unitCategory = self::$conversionUnits[$uom]['Group']; + + return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']]; + } + + $multiplierType .= substr($uom, 0, 1); + $uom = substr($uom, 1); + + // Check 2-character standard metric multiplier prefixes + if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) { + if (self::$conversionUnits[$uom]['AllowPrefix'] === false) { + throw new Exception('Prefix not allowed for UoM'); + } + $unitCategory = self::$conversionUnits[$uom]['Group']; + + return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']]; + } + + // Check 2-character binary multiplier prefixes + if (isset(self::$conversionUnits[$uom], self::$binaryConversionMultipliers[$multiplierType])) { + if (self::$conversionUnits[$uom]['AllowPrefix'] === false) { + throw new Exception('Prefix not allowed for UoM'); + } + $unitCategory = self::$conversionUnits[$uom]['Group']; + if ($unitCategory !== 'Information') { + throw new Exception('Binary Prefix is only allowed for Information UoM'); + } + + return [$uom, $unitCategory, self::$binaryConversionMultipliers[$multiplierType]['multiplier']]; + } + + throw new Exception('UoM Not Found'); + } + + /** + * @param float|int $value + * + * @return float|int + */ + protected static function convertTemperature(string $fromUOM, string $toUOM, $value) + { + $fromUOM = self::resolveTemperatureSynonyms($fromUOM); + $toUOM = self::resolveTemperatureSynonyms($toUOM); + + if ($fromUOM === $toUOM) { + return $value; + } + + // Convert to Kelvin + switch ($fromUOM) { + case 'F': + $value = ($value - 32) / 1.8 + 273.15; + + break; + case 'C': + $value += 273.15; + + break; + case 'Rank': + $value /= 1.8; + + break; + case 'Reau': + $value = $value * 1.25 + 273.15; + + break; + } + + // Convert from Kelvin + switch ($toUOM) { + case 'F': + $value = ($value - 273.15) * 1.8 + 32.00; + + break; + case 'C': + $value -= 273.15; + + break; + case 'Rank': + $value *= 1.8; + + break; + case 'Reau': + $value = ($value - 273.15) * 0.80000; + + break; + } + + return $value; + } + + private static function resolveTemperatureSynonyms(string $uom) + { + switch ($uom) { + case 'fah': + return 'F'; + case 'cel': + return 'C'; + case 'kel': + return 'K'; + } + + return $uom; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php index fccf0af70fd..87c7d222be0 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php @@ -15,7 +15,7 @@ class Exception extends PhpSpreadsheetException * @param mixed $line * @param mixed $context */ - public static function errorHandlerCallback($code, $string, $file, $line, $context) + public static function errorHandlerCallback($code, $string, $file, $line, $context): void { $e = new self($string, $code); $e->line = $line; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php index 90bb36df74f..5a908aa5136 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; class Financial { - const FINANCIAL_MAX_ITERATIONS = 32; + const FINANCIAL_MAX_ITERATIONS = 128; const FINANCIAL_PRECISION = 1.0e-08; @@ -50,10 +50,6 @@ class Financial if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) { return true; } - if ((Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) && - (($frequency == 6) || ($frequency == 12))) { - return true; - } return false; } @@ -71,7 +67,7 @@ class Financial * 3 365 * 4 European 360 * - * @return int + * @return int|string Result, or a string containing an error */ private static function daysPerYear($year, $basis = 0) { @@ -118,8 +114,6 @@ class Financial * Excel Function: * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $issue the security's issue date * @param mixed $firstinterest the security's first interest date * @param mixed $settlement The security's settlement date. @@ -133,10 +127,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -144,7 +134,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0) { @@ -183,8 +173,6 @@ class Financial * Excel Function: * ACCRINTM(issue,settlement,rate[,par[,basis]]) * - * @category Financial Functions - * * @param mixed $issue The security's issue date * @param mixed $settlement The security's settlement (or maturity) date * @param float $rate The security's annual coupon rate @@ -197,7 +185,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0) { @@ -241,8 +229,6 @@ class Financial * Excel Function: * AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) * - * @category Financial Functions - * * @param float $cost The cost of the asset * @param mixed $purchased Date of the purchase of the asset * @param mixed $firstPeriod Date of the end of the first period @@ -319,8 +305,6 @@ class Financial * Excel Function: * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) * - * @category Financial Functions - * * @param float $cost The cost of the asset * @param mixed $purchased Date of the purchase of the asset * @param mixed $firstPeriod Date of the end of the first period @@ -378,8 +362,6 @@ class Financial * Excel Function: * COUPDAYBS(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -390,10 +372,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -417,15 +395,21 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis); $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false); + if ($basis == 1) { + return abs(DateTime::DAYS($prev, $settlement)); + } + return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear; } @@ -437,8 +421,6 @@ class Financial * Excel Function: * COUPDAYS(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -449,10 +431,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -476,9 +454,11 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } @@ -511,8 +491,6 @@ class Financial * Excel Function: * COUPDAYSNC(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -523,10 +501,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -550,9 +524,11 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } @@ -570,8 +546,6 @@ class Financial * Excel Function: * COUPNCD(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -582,10 +556,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -610,9 +580,11 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } @@ -628,8 +600,6 @@ class Financial * Excel Function: * COUPNUM(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -640,10 +610,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -667,25 +633,17 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } - $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis); - $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * $daysPerYear; + $yearsBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, 0); - switch ($frequency) { - case 1: // annual payments - case 2: // half-yearly - case 4: // quarterly - case 6: // bimonthly - case 12: // monthly - return ceil($daysBetweenSettlementAndMaturity / $daysPerYear * $frequency); - } - - return Functions::VALUE(); + return ceil($yearsBetweenSettlementAndMaturity * $frequency); } /** @@ -696,8 +654,6 @@ class Financial * Excel Function: * COUPPCD(settlement,maturity,frequency[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -708,10 +664,6 @@ class Financial * 1 Annual * 2 Semi-Annual * 4 Quarterly - * If working in Gnumeric Mode, the following frequency options are - * also available - * 6 Bimonthly - * 12 Monthly * @param int $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual @@ -736,9 +688,11 @@ class Financial return Functions::VALUE(); } - if (($settlement >= $maturity) || + if ( + ($settlement >= $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } @@ -753,8 +707,6 @@ class Financial * Excel Function: * CUMIPMT(rate,nper,pv,start,end[,type]) * - * @category Financial Functions - * * @param float $rate The Interest rate * @param int $nper The total number of payment periods * @param float $pv Present Value @@ -801,8 +753,6 @@ class Financial * Excel Function: * CUMPRINC(rate,nper,pv,start,end[,type]) * - * @category Financial Functions - * * @param float $rate The Interest rate * @param int $nper The total number of payment periods * @param float $pv Present Value @@ -854,8 +804,6 @@ class Financial * Excel Function: * DB(cost,salvage,life,period[,month]) * - * @category Financial Functions - * * @param float $cost Initial cost of the asset * @param float $salvage Value at the end of the depreciation. * (Sometimes called the salvage value of the asset) @@ -889,11 +837,12 @@ class Financial return Functions::NAN(); } // Set Fixed Depreciation Rate - $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life)); + $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life); $fixedDepreciationRate = round($fixedDepreciationRate, 3); // Loop through each period calculating the depreciation $previousDepreciation = 0; + $depreciation = 0; for ($per = 1; $per <= $period; ++$per) { if ($per == 1) { $depreciation = $cost * $fixedDepreciationRate * $month / 12; @@ -904,9 +853,6 @@ class Financial } $previousDepreciation += $depreciation; } - if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) { - $depreciation = round($depreciation, 2); - } return $depreciation; } @@ -923,8 +869,6 @@ class Financial * Excel Function: * DDB(cost,salvage,life,period[,factor]) * - * @category Financial Functions - * * @param float $cost Initial cost of the asset * @param float $salvage Value at the end of the depreciation. * (Sometimes called the salvage value of the asset) @@ -957,18 +901,16 @@ class Financial return Functions::NAN(); } // Set Fixed Depreciation Rate - $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life)); + $fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life); $fixedDepreciationRate = round($fixedDepreciationRate, 3); // Loop through each period calculating the depreciation $previousDepreciation = 0; + $depreciation = 0; for ($per = 1; $per <= $period; ++$per) { $depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation)); $previousDepreciation += $depreciation; } - if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) { - $depreciation = round($depreciation, 2); - } return $depreciation; } @@ -984,8 +926,6 @@ class Financial * Excel Function: * DISC(settlement,maturity,price,redemption[,basis]) * - * @category Financial Functions - * * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue * date when the security is traded to the buyer. @@ -1040,8 +980,6 @@ class Financial * Excel Function: * DOLLARDE(fractional_dollar,fraction) * - * @category Financial Functions - * * @param float $fractional_dollar Fractional Dollar * @param int $fraction Fraction * @@ -1063,7 +1001,7 @@ class Financial $dollars = floor($fractional_dollar); $cents = fmod($fractional_dollar, 1); $cents /= $fraction; - $cents *= pow(10, ceil(log10($fraction))); + $cents *= 10 ** ceil(log10($fraction)); return $dollars + $cents; } @@ -1078,8 +1016,6 @@ class Financial * Excel Function: * DOLLARFR(decimal_dollar,fraction) * - * @category Financial Functions - * * @param float $decimal_dollar Decimal Dollar * @param int $fraction Fraction * @@ -1101,7 +1037,7 @@ class Financial $dollars = floor($decimal_dollar); $cents = fmod($decimal_dollar, 1); $cents *= $fraction; - $cents *= pow(10, -ceil(log10($fraction))); + $cents *= 10 ** (-ceil(log10($fraction))); return $dollars + $cents; } @@ -1115,8 +1051,6 @@ class Financial * Excel Function: * EFFECT(nominal_rate,npery) * - * @category Financial Functions - * * @param float $nominal_rate Nominal interest rate * @param int $npery Number of compounding payments per year * @@ -1132,7 +1066,7 @@ class Financial return Functions::NAN(); } - return pow((1 + $nominal_rate / $npery), $npery) - 1; + return (1 + $nominal_rate / $npery) ** $npery - 1; } /** @@ -1143,8 +1077,6 @@ class Financial * Excel Function: * FV(rate,nper,pmt[,pv[,type]]) * - * @category Financial Functions - * * @param float $rate The interest rate per period * @param int $nper Total number of payment periods in an annuity * @param float $pmt The payment made each period: it cannot change over the @@ -1173,7 +1105,7 @@ class Financial // Calculate if ($rate !== null && $rate != 0) { - return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate; + return -$pv * (1 + $rate) ** $nper - $pmt * (1 + $rate * $type) * ((1 + $rate) ** $nper - 1) / $rate; } return -$pv - $pmt * $nper; @@ -1424,7 +1356,7 @@ class Financial * @param float $finance_rate The interest rate you pay on the money used in the cash flows * @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function MIRR($values, $finance_rate, $reinvestment_rate) { @@ -1442,9 +1374,9 @@ class Financial $npv_pos = $npv_neg = 0.0; foreach ($values as $i => $v) { if ($v >= 0) { - $npv_pos += $v / pow($rr, $i); + $npv_pos += $v / $rr ** $i; } else { - $npv_neg += $v / pow($fr, $i); + $npv_neg += $v / $fr ** $i; } } @@ -1452,8 +1384,8 @@ class Financial return Functions::VALUE(); } - $mirr = pow((-$npv_pos * pow($rr, $n)) - / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0; + $mirr = ((-$npv_pos * $rr ** $n) + / ($npv_neg * ($rr))) ** (1.0 / ($n - 1)) - 1.0; return is_finite($mirr) ? $mirr : Functions::VALUE(); } @@ -1466,7 +1398,7 @@ class Financial * @param float $effect_rate Effective interest rate * @param int $npery Number of compounding payments per year * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function NOMINAL($effect_rate = 0, $npery = 0) { @@ -1479,7 +1411,7 @@ class Financial } // Calculate - return $npery * (pow($effect_rate + 1, 1 / $npery) - 1); + return $npery * (($effect_rate + 1) ** (1 / $npery) - 1); } /** @@ -1493,7 +1425,7 @@ class Financial * @param float $fv Future Value * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) { @@ -1544,7 +1476,7 @@ class Financial for ($i = 1; $i <= $countArgs; ++$i) { // Is it a numeric value? if (is_numeric($aArgs[$i - 1])) { - $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i); + $returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i; } } @@ -1561,7 +1493,7 @@ class Financial * @param float $pv Present Value * @param float $fv Future Value * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function PDURATION($rate = 0, $pv = 0, $fv = 0) { @@ -1590,7 +1522,7 @@ class Financial * @param float $fv Future Value * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period * - * @return float + * @return float|string Result, or a string containing an error */ public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) { @@ -1607,7 +1539,7 @@ class Financial // Calculate if ($rate !== null && $rate != 0) { - return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate); + return (-$fv - $pv * (1 + $rate) ** $nper) / (1 + $rate * $type) / (((1 + $rate) ** $nper - 1) / $rate); } return (-$pv - $fv) / $nper; @@ -1625,7 +1557,7 @@ class Financial * @param float $fv Future Value * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period * - * @return float + * @return float|string Result, or a string containing an error */ public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) { @@ -1650,26 +1582,60 @@ class Financial return $interestAndPrincipal[1]; } + private static function validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis) + { + if (is_string($settlement)) { + return Functions::VALUE(); + } + if (is_string($maturity)) { + return Functions::VALUE(); + } + if (!is_numeric($rate)) { + return Functions::VALUE(); + } + if (!is_numeric($yield)) { + return Functions::VALUE(); + } + if (!is_numeric($redemption)) { + return Functions::VALUE(); + } + if (!is_numeric($frequency)) { + return Functions::VALUE(); + } + if (!is_numeric($basis)) { + return Functions::VALUE(); + } + + return ''; + } + public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0) { $settlement = Functions::flattenSingleValue($settlement); $maturity = Functions::flattenSingleValue($maturity); - $rate = (float) Functions::flattenSingleValue($rate); - $yield = (float) Functions::flattenSingleValue($yield); - $redemption = (float) Functions::flattenSingleValue($redemption); - $frequency = (int) Functions::flattenSingleValue($frequency); - $basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis); + $rate = Functions::flattenSingleValue($rate); + $yield = Functions::flattenSingleValue($yield); + $redemption = Functions::flattenSingleValue($redemption); + $frequency = Functions::flattenSingleValue($frequency); + $basis = Functions::flattenSingleValue($basis); - if (is_string($settlement = DateTime::getDateValue($settlement))) { - return Functions::VALUE(); - } - if (is_string($maturity = DateTime::getDateValue($maturity))) { - return Functions::VALUE(); + $settlement = DateTime::getDateValue($settlement); + $maturity = DateTime::getDateValue($maturity); + $rslt = self::validatePrice($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis); + if ($rslt) { + return $rslt; } + $rate = (float) $rate; + $yield = (float) $yield; + $redemption = (float) $redemption; + $frequency = (int) $frequency; + $basis = (int) $basis; - if (($settlement > $maturity) || + if ( + ($settlement > $maturity) || (!self::isValidFrequency($frequency)) || - (($basis < 0) || ($basis > 4))) { + (($basis < 0) || ($basis > 4)) + ) { return Functions::NAN(); } @@ -1682,9 +1648,9 @@ class Financial $rfp = 100 * ($rate / $frequency); $de = $dsc / $e; - $result = $redemption / pow($baseYF, (--$n + $de)); + $result = $redemption / $baseYF ** (--$n + $de); for ($k = 0; $k <= $n; ++$k) { - $result += $rfp / (pow($baseYF, ($k + $de))); + $result += $rfp / ($baseYF ** ($k + $de)); } $result -= $rfp * ($a / $e); @@ -1709,7 +1675,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float + * @return float|string Result, or a string containing an error */ public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0) { @@ -1755,7 +1721,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float + * @return float|string Result, or a string containing an error */ public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0) { @@ -1813,7 +1779,7 @@ class Financial * @param float $fv Future Value * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period * - * @return float + * @return float|string Result, or a string containing an error */ public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) { @@ -1830,7 +1796,7 @@ class Financial // Calculate if ($rate !== null && $rate != 0) { - return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper); + return (-$pmt * (1 + $rate * $type) * (((1 + $rate) ** $nper - 1) / $rate) - $fv) / (1 + $rate) ** $nper; } return -$fv - $pmt * $nper; @@ -1847,8 +1813,6 @@ class Financial * Excel Function: * RATE(nper,pmt,pv[,fv[,type[,guess]]]) * - * @category Financial Functions - * * @param float $nper The total number of payment periods in an annuity * @param float $pmt The payment made each period and cannot change over the life * of the annuity. @@ -1865,7 +1829,7 @@ class Financial * @param float $guess Your guess for what the rate will be. * If you omit guess, it is assumed to be 10 percent. * - * @return float + * @return float|string */ public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) { @@ -1877,38 +1841,39 @@ class Financial $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess); $rate = $guess; - if (abs($rate) < self::FINANCIAL_PRECISION) { - $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv; - } else { - $f = exp($nper * log(1 + $rate)); - $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv; - } - $y0 = $pv + $pmt * $nper + $fv; - $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv; - - // find root by secant method - $i = $x0 = 0.0; - $x1 = $rate; - while ((abs($y0 - $y1) > self::FINANCIAL_PRECISION) && ($i < self::FINANCIAL_MAX_ITERATIONS)) { - $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0); - $x0 = $x1; - $x1 = $rate; - if (($nper * abs($pmt)) > ($pv - $fv)) { - $x1 = abs($x1); + // rest of code adapted from python/numpy + $close = false; + $iter = 0; + while (!$close && $iter < self::FINANCIAL_MAX_ITERATIONS) { + $nextdiff = self::rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type); + if (!is_numeric($nextdiff)) { + break; } - if (abs($rate) < self::FINANCIAL_PRECISION) { - $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv; - } else { - $f = exp($nper * log(1 + $rate)); - $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv; - } - - $y0 = $y1; - $y1 = $y; - ++$i; + $rate1 = $rate - $nextdiff; + $close = abs($rate1 - $rate) < self::FINANCIAL_PRECISION; + ++$iter; + $rate = $rate1; } - return $rate; + return $close ? $rate : Functions::NAN(); + } + + private static function rateNextGuess($rate, $nper, $pmt, $pv, $fv, $type) + { + if ($rate == 0) { + return Functions::NAN(); + } + $tt1 = ($rate + 1) ** $nper; + $tt2 = ($rate + 1) ** ($nper - 1); + $numerator = $fv + $tt1 * $pv + $pmt * ($tt1 - 1) * ($rate * $type + 1) / $rate; + $denominator = $nper * $tt2 * $pv - $pmt * ($tt1 - 1) * ($rate * $type + 1) / ($rate * $rate) + + $nper * $pmt * $tt2 * ($rate * $type + 1) / $rate + + $pmt * ($tt1 - 1) * $type / $rate; + if ($denominator == 0) { + return Functions::NAN(); + } + + return $numerator / $denominator; } /** @@ -1929,7 +1894,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float + * @return float|string Result, or a string containing an error */ public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0) { @@ -1965,7 +1930,7 @@ class Financial * @param float $pv Present Value * @param float $fv Future Value * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function RRI($nper = 0, $pv = 0, $fv = 0) { @@ -1980,7 +1945,7 @@ class Financial return Functions::NAN(); } - return pow($fv / $pv, 1 / $nper) - 1; + return ($fv / $pv) ** (1 / $nper) - 1; } /** @@ -1992,7 +1957,7 @@ class Financial * @param mixed $salvage Value at the end of the depreciation * @param mixed $life Number of periods over which the asset is depreciated * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function SLN($cost, $salvage, $life) { @@ -2022,7 +1987,7 @@ class Financial * @param mixed $life Number of periods over which the asset is depreciated * @param mixed $period Period * - * @return float|string + * @return float|string Result, or a string containing an error */ public static function SYD($cost, $salvage, $life, $period) { @@ -2054,7 +2019,7 @@ class Financial * The maturity date is the date when the Treasury bill expires. * @param int $discount The Treasury bill's discount rate * - * @return float + * @return float|string Result, or a string containing an error */ public static function TBILLEQ($settlement, $maturity, $discount) { @@ -2093,7 +2058,7 @@ class Financial * The maturity date is the date when the Treasury bill expires. * @param int $discount The Treasury bill's discount rate * - * @return float + * @return float|string Result, or a string containing an error */ public static function TBILLPRICE($settlement, $maturity, $discount) { @@ -2183,6 +2148,84 @@ class Financial return Functions::VALUE(); } + private static function bothNegAndPos($neg, $pos) + { + return $neg && $pos; + } + + private static function xirrPart2(&$values) + { + $valCount = count($values); + $foundpos = false; + $foundneg = false; + for ($i = 0; $i < $valCount; ++$i) { + $fld = $values[$i]; + if (!is_numeric($fld)) { + return Functions::VALUE(); + } elseif ($fld > 0) { + $foundpos = true; + } elseif ($fld < 0) { + $foundneg = true; + } + } + if (!self::bothNegAndPos($foundneg, $foundpos)) { + return Functions::NAN(); + } + + return ''; + } + + private static function xirrPart1(&$values, &$dates) + { + if ((!is_array($values)) && (!is_array($dates))) { + return Functions::NA(); + } + $values = Functions::flattenArray($values); + $dates = Functions::flattenArray($dates); + if (count($values) != count($dates)) { + return Functions::NAN(); + } + + $datesCount = count($dates); + for ($i = 0; $i < $datesCount; ++$i) { + $dates[$i] = DateTime::getDateValue($dates[$i]); + if (!is_numeric($dates[$i])) { + return Functions::VALUE(); + } + } + + return self::xirrPart2($values); + } + + private static function xirrPart3($values, $dates, $x1, $x2) + { + $f = self::xnpvOrdered($x1, $values, $dates, false); + if ($f < 0.0) { + $rtb = $x1; + $dx = $x2 - $x1; + } else { + $rtb = $x2; + $dx = $x1 - $x2; + } + + $rslt = Functions::VALUE(); + for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) { + $dx *= 0.5; + $x_mid = $rtb + $dx; + $f_mid = self::xnpvOrdered($x_mid, $values, $dates, false); + if ($f_mid <= 0.0) { + $rtb = $x_mid; + } + if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) { + $rslt = $x_mid; + + break; + } + } + + return $rslt; + } + /** * XIRR. * @@ -2202,73 +2245,37 @@ class Financial */ public static function XIRR($values, $dates, $guess = 0.1) { - if ((!is_array($values)) && (!is_array($dates))) { - return Functions::VALUE(); - } - $values = Functions::flattenArray($values); - $dates = Functions::flattenArray($dates); - $guess = Functions::flattenSingleValue($guess); - if (count($values) != count($dates)) { - return Functions::NAN(); - } - - $datesCount = count($dates); - for ($i = 0; $i < $datesCount; ++$i) { - $dates[$i] = DateTime::getDateValue($dates[$i]); - if (!is_numeric($dates[$i])) { - return Functions::VALUE(); - } - } - if (min($dates) != $dates[0]) { - return Functions::NAN(); + $rslt = self::xirrPart1($values, $dates); + if ($rslt) { + return $rslt; } // create an initial range, with a root somewhere between 0 and guess + $guess = Functions::flattenSingleValue($guess); $x1 = 0.0; - $x2 = $guess; - $f1 = self::XNPV($x1, $values, $dates); - if (!is_numeric($f1)) { - return $f1; - } - $f2 = self::XNPV($x2, $values, $dates); - if (!is_numeric($f2)) { - return $f2; - } + $x2 = $guess ? $guess : 0.1; + $f1 = self::xnpvOrdered($x1, $values, $dates, false); + $f2 = self::xnpvOrdered($x2, $values, $dates, false); + $found = false; for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) { + if (!is_numeric($f1) || !is_numeric($f2)) { + break; + } if (($f1 * $f2) < 0.0) { + $found = true; + break; } elseif (abs($f1) < abs($f2)) { - $f1 = self::XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates); + $f1 = self::xnpvOrdered($x1 += 1.6 * ($x1 - $x2), $values, $dates, false); } else { - $f2 = self::XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates); + $f2 = self::xnpvOrdered($x2 += 1.6 * ($x2 - $x1), $values, $dates, false); } } - if (($f1 * $f2) > 0.0) { + if (!$found) { return Functions::NAN(); } - $f = self::XNPV($x1, $values, $dates); - if ($f < 0.0) { - $rtb = $x1; - $dx = $x2 - $x1; - } else { - $rtb = $x2; - $dx = $x1 - $x2; - } - - for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) { - $dx *= 0.5; - $x_mid = $rtb + $dx; - $f_mid = self::XNPV($x_mid, $values, $dates); - if ($f_mid <= 0.0) { - $rtb = $x_mid; - } - if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) { - return $x_mid; - } - } - - return Functions::VALUE(); + return self::xirrPart3($values, $dates, $x1, $x2); } /** @@ -2293,32 +2300,61 @@ class Financial */ public static function XNPV($rate, $values, $dates) { - $rate = Functions::flattenSingleValue($rate); + return self::xnpvOrdered($rate, $values, $dates, true); + } + + private static function validateXnpv($rate, $values, $dates) + { if (!is_numeric($rate)) { return Functions::VALUE(); } - if ((!is_array($values)) || (!is_array($dates))) { - return Functions::VALUE(); - } - $values = Functions::flattenArray($values); - $dates = Functions::flattenArray($dates); $valCount = count($values); if ($valCount != count($dates)) { return Functions::NAN(); } - if ((min($values) > 0) || (max($values) < 0)) { + if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) { return Functions::NAN(); } + $date0 = DateTime::getDateValue($dates[0]); + if (is_string($date0)) { + return Functions::VALUE(); + } + return ''; + } + + private static function xnpvOrdered($rate, $values, $dates, $ordered = true) + { + $rate = Functions::flattenSingleValue($rate); + $values = Functions::flattenArray($values); + $dates = Functions::flattenArray($dates); + $valCount = count($values); + $date0 = DateTime::getDateValue($dates[0]); + $rslt = self::validateXnpv($rate, $values, $dates); + if ($rslt) { + return $rslt; + } $xnpv = 0.0; for ($i = 0; $i < $valCount; ++$i) { if (!is_numeric($values[$i])) { return Functions::VALUE(); } - $xnpv += $values[$i] / pow(1 + $rate, DateTime::DATEDIF($dates[0], $dates[$i], 'd') / 365); + $datei = DateTime::getDateValue($dates[$i]); + if (is_string($datei)) { + return Functions::VALUE(); + } + if ($date0 > $datei) { + $dif = $ordered ? Functions::NAN() : -DateTime::DATEDIF($datei, $date0, 'd'); + } else { + $dif = DateTime::DATEDIF($date0, $datei, 'd'); + } + if (!is_numeric($dif)) { + return $dif; + } + $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365); } - return (is_finite($xnpv)) ? $xnpv : Functions::VALUE(); + return is_finite($xnpv) ? $xnpv : Functions::VALUE(); } /** @@ -2339,7 +2375,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float + * @return float|string Result, or a string containing an error */ public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0) { @@ -2390,7 +2426,7 @@ class Financial * 3 Actual/365 * 4 European 30/360 * - * @return float + * @return float|string Result, or a string containing an error */ public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php index 9b3c66e9e86..c11af834ff8 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php @@ -62,8 +62,6 @@ class FormulaParser * Create a new FormulaParser. * * @param string $pFormula Formula to parse - * - * @throws Exception */ public function __construct($pFormula = '') { @@ -93,8 +91,6 @@ class FormulaParser * * @param int $pId Token id * - * @throws Exception - * * @return string */ public function getToken($pId = 0) @@ -129,7 +125,7 @@ class FormulaParser /** * Parse to tokens. */ - private function parseToTokens() + private function parseToTokens(): void { // No attempt is made to verify formulas; assumes formulas are derived from Excel, where // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions. @@ -494,11 +490,13 @@ class FormulaParser continue; } - if (!( + if ( + !( (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND) - )) { + ) + ) { continue; } @@ -506,11 +504,13 @@ class FormulaParser continue; } - if (!( + if ( + !( (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) || (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) || ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND) - )) { + ) + ) { continue; } @@ -542,12 +542,14 @@ class FormulaParser if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') { if ($i == 0) { $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX); - } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && + } elseif ( + (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) || - ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) { + ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND) + ) { $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH); } else { $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX); @@ -561,12 +563,14 @@ class FormulaParser if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '+') { if ($i == 0) { continue; - } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && + } elseif ( + (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) || ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) || - ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) { + ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND) + ) { $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH); } else { continue; @@ -577,8 +581,10 @@ class FormulaParser continue; } - if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && - $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) { + if ( + $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && + $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING + ) { if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) { $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL); } elseif ($token->getValue() == '&') { @@ -592,8 +598,10 @@ class FormulaParser continue; } - if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND && - $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) { + if ( + $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND && + $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING + ) { if (!is_numeric($token->getValue())) { if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') { $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php index 66618d4a0c0..4d225de2d5c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php @@ -103,7 +103,7 @@ class FormulaToken * * @param string $value */ - public function setValue($value) + public function setValue($value): void { $this->value = $value; } @@ -123,7 +123,7 @@ class FormulaToken * * @param string $value */ - public function setTokenType($value) + public function setTokenType($value): void { $this->tokenType = $value; } @@ -143,7 +143,7 @@ class FormulaToken * * @param string $value */ - public function setTokenSubType($value) + public function setTokenSubType($value): void { $this->tokenSubType = $value; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php index bb2170be1fc..2e8a7ecfccc 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php @@ -57,8 +57,6 @@ class Functions /** * Set the Compatibility Mode. * - * @category Function Configuration - * * @param string $compatibilityMode Compatibility Mode * Permitted values are: * Functions::COMPATIBILITY_EXCEL 'Excel' @@ -69,7 +67,8 @@ class Functions */ public static function setCompatibilityMode($compatibilityMode) { - if (($compatibilityMode == self::COMPATIBILITY_EXCEL) || + if ( + ($compatibilityMode == self::COMPATIBILITY_EXCEL) || ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) || ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE) ) { @@ -84,8 +83,6 @@ class Functions /** * Return the current Compatibility Mode. * - * @category Function Configuration - * * @return string Compatibility Mode * Possible Return values are: * Functions::COMPATIBILITY_EXCEL 'Excel' @@ -100,8 +97,6 @@ class Functions /** * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object). * - * @category Function Configuration - * * @param string $returnDateType Return Date Format * Permitted values are: * Functions::RETURNDATE_UNIX_TIMESTAMP 'P' @@ -112,7 +107,8 @@ class Functions */ public static function setReturnDateType($returnDateType) { - if (($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) || + if ( + ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) || ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) || ($returnDateType == self::RETURNDATE_EXCEL) ) { @@ -127,8 +123,6 @@ class Functions /** * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object). * - * @category Function Configuration - * * @return string Return Date Format * Possible Return values are: * Functions::RETURNDATE_UNIX_TIMESTAMP 'P' @@ -143,8 +137,6 @@ class Functions /** * DUMMY. * - * @category Error Returns - * * @return string #Not Yet Implemented */ public static function DUMMY() @@ -155,8 +147,6 @@ class Functions /** * DIV0. * - * @category Error Returns - * * @return string #Not Yet Implemented */ public static function DIV0() @@ -173,8 +163,6 @@ class Functions * Returns the error value #N/A * #N/A is the error value that means "no value is available." * - * @category Logical Functions - * * @return string #N/A! */ public static function NA() @@ -187,8 +175,6 @@ class Functions * * Returns the error value #NUM! * - * @category Error Returns - * * @return string #NUM! */ public static function NAN() @@ -201,8 +187,6 @@ class Functions * * Returns the error value #NAME? * - * @category Error Returns - * * @return string #NAME? */ public static function NAME() @@ -215,8 +199,6 @@ class Functions * * Returns the error value #REF! * - * @category Error Returns - * * @return string #REF! */ public static function REF() @@ -229,8 +211,6 @@ class Functions * * Returns the error value #NULL! * - * @category Error Returns - * * @return string #NULL! */ public static function null() @@ -243,8 +223,6 @@ class Functions * * Returns the error value #VALUE! * - * @category Error Returns - * * @return string #VALUE! */ public static function VALUE() @@ -646,7 +624,7 @@ class Functions public static function flattenSingleValue($value = '') { while (is_array($value)) { - $value = array_pop($value); + $value = array_shift($value); } return $value; @@ -660,7 +638,7 @@ class Functions * * @return bool|string */ - public static function isFormula($cellReference = '', Cell $pCell = null) + public static function isFormula($cellReference = '', ?Cell $pCell = null) { if ($pCell === null) { return self::REF(); @@ -669,7 +647,7 @@ class Functions preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches); $cellReference = $matches[6] . $matches[7]; - $worksheetName = trim($matches[3], "'"); + $worksheetName = str_replace("''", "'", trim($matches[2], "'")); $worksheet = (!empty($worksheetName)) ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php index 416d119ac8e..69c543cef99 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php @@ -12,8 +12,6 @@ class Logical * Excel Function: * =TRUE() * - * @category Logical Functions - * * @return bool True */ public static function true() @@ -29,8 +27,6 @@ class Logical * Excel Function: * =FALSE() * - * @category Logical Functions - * * @return bool False */ public static function false() @@ -80,8 +76,6 @@ class Logical * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value * - * @category Logical Functions - * * @param mixed ...$args Data values * * @return bool|string the logical AND of the arguments @@ -123,8 +117,6 @@ class Logical * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value * - * @category Logical Functions - * * @param mixed $args Data values * * @return bool|string the logical OR of the arguments @@ -166,8 +158,6 @@ class Logical * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value * - * @category Logical Functions - * * @param mixed $args Data values * * @return bool|string the logical XOR of the arguments @@ -207,8 +197,6 @@ class Logical * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value * - * @category Logical Functions - * * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE * * @return bool|string the boolean inverse of the argument @@ -256,8 +244,6 @@ class Logical * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned. * ReturnIfFalse can be another formula. * - * @category Logical Functions - * * @param mixed $condition Condition to evaluate * @param mixed $returnIfTrue Value to return when condition is true * @param mixed $returnIfFalse Optional value to return when condition is false @@ -294,8 +280,6 @@ class Logical * default * Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n). * - * @category Logical Functions - * * @param mixed $arguments Statement arguments * * @return mixed The value of matched expression @@ -337,8 +321,6 @@ class Logical * Excel Function: * =IFERROR(testValue,errorpart) * - * @category Logical Functions - * * @param mixed $testValue Value to check, is also the value returned when no error * @param mixed $errorpart Value to return when testValue is an error condition * @@ -358,8 +340,6 @@ class Logical * Excel Function: * =IFNA(testValue,napart) * - * @category Logical Functions - * * @param mixed $testValue Value to check, is also the value returned when not an NA * @param mixed $napart Value to return when testValue is an NA condition * @@ -372,4 +352,39 @@ class Logical return self::statementIf(Functions::isNa($testValue), $napart, $testValue); } + + /** + * IFS. + * + * Excel Function: + * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n) + * + * testValue1 ... testValue_n + * Conditions to Evaluate + * returnIfTrue1 ... returnIfTrue_n + * Value returned if corresponding testValue (nth) was true + * + * @param mixed ...$arguments Statement arguments + * + * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true + */ + public static function IFS(...$arguments) + { + if (count($arguments) % 2 != 0) { + return Functions::NA(); + } + // We use instance of Exception as a falseValue in order to prevent string collision with value in cell + $falseValueException = new Exception(); + for ($i = 0; $i < count($arguments); $i += 2) { + $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]); + $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]); + $result = self::statementIf($testValue, $returnIfTrue, $falseValueException); + + if ($result !== $falseValueException) { + return $result; + } + } + + return Functions::NA(); + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php index 48434300b29..45aa9239641 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php @@ -126,7 +126,7 @@ class LookupRef * * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns * - * @return int The number of columns in cellAddress + * @return int|string The number of columns in cellAddress, or a string if arguments are invalid */ public static function COLUMNS($cellAddress = null) { @@ -160,7 +160,7 @@ class LookupRef * * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers * - * @return int or array of integer + * @return int|mixed[]|string */ public static function ROW($cellAddress = null) { @@ -203,7 +203,7 @@ class LookupRef * * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows * - * @return int The number of rows in cellAddress + * @return int|string The number of rows in cellAddress, or a string if arguments are invalid */ public static function ROWS($cellAddress = null) { @@ -230,15 +230,13 @@ class LookupRef * Excel Function: * =HYPERLINK(linkURL,displayName) * - * @category Logical Functions - * * @param string $linkURL Value to check, is also the value returned when no error * @param string $displayName Value to return when testValue is an error condition * @param Cell $pCell The cell to set the hyperlink in * * @return mixed The value of $displayName (or $linkURL if $displayName was blank) */ - public static function HYPERLINK($linkURL = '', $displayName = null, Cell $pCell = null) + public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $pCell = null) { $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL); $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName); @@ -273,9 +271,9 @@ class LookupRef * * @return mixed The cells referenced by cellAddress * - * @todo Support for the optional a1 parameter introduced in Excel 2010 + * @TODO Support for the optional a1 parameter introduced in Excel 2010 */ - public static function INDIRECT($cellAddress = null, Cell $pCell = null) + public static function INDIRECT($cellAddress = null, ?Cell $pCell = null) { $cellAddress = Functions::flattenSingleValue($cellAddress); if ($cellAddress === null || $cellAddress === '') { @@ -288,9 +286,11 @@ class LookupRef [$cellAddress1, $cellAddress2] = explode(':', $cellAddress); } - if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) || - (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))) { - if (!preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $cellAddress1, $matches)) { + if ( + (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) || + (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches))) + ) { + if (!preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $cellAddress1, $matches)) { return Functions::REF(); } @@ -339,11 +339,10 @@ class LookupRef * starting reference). * @param mixed $height The height, in number of rows, that you want the returned reference to be. Height must be a positive number. * @param mixed $width The width, in number of columns, that you want the returned reference to be. Width must be a positive number. - * @param null|Cell $pCell * * @return string A reference to a cell or range of cells */ - public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, Cell $pCell = null) + public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $pCell = null) { $rows = Functions::flattenSingleValue($rows); $columns = Functions::flattenSingleValue($columns); @@ -419,14 +418,6 @@ class LookupRef * Excel Function: * =CHOOSE(index_num, value1, [value2], ...) * - * @param mixed $index_num Specifies which value argument is selected. - * Index_num must be a number between 1 and 254, or a formula or reference to a cell containing a number - * between 1 and 254. - * @param mixed $value1 ... Value1 is required, subsequent values are optional. - * Between 1 to 254 value arguments from which CHOOSE selects a value or an action to perform based on - * index_num. The arguments can be numbers, cell references, defined names, formulas, functions, or - * text. - * * @return mixed The selected value */ public static function CHOOSE(...$chooseArgs) @@ -496,10 +487,18 @@ class LookupRef return Functions::NA(); } + if ($matchType == 1) { + // If match_type is 1 the list has to be processed from last to first + + $lookupArray = array_reverse($lookupArray); + $keySet = array_reverse(array_keys($lookupArray)); + } + // Lookup_array should contain only number, text, or logical values, or empty (null) cells foreach ($lookupArray as $i => $lookupArrayValue) { // check the type of the value - if ((!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) && + if ( + (!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) && (!is_bool($lookupArrayValue)) && ($lookupArrayValue !== null) ) { return Functions::NA(); @@ -509,24 +508,17 @@ class LookupRef $lookupArray[$i] = StringHelper::strToLower($lookupArrayValue); } if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) { - $lookupArray = array_slice($lookupArray, 0, $i - 1); + unset($lookupArray[$i]); } } - if ($matchType == 1) { - // If match_type is 1 the list has to be processed from last to first - - $lookupArray = array_reverse($lookupArray); - $keySet = array_reverse(array_keys($lookupArray)); - } - // ** // find the match // ** if ($matchType === 0 || $matchType === 1) { foreach ($lookupArray as $i => $lookupArrayValue) { - $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue); + $typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) || (is_numeric($lookupValue) && is_numeric($lookupArrayValue))); $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue; $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue; $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch; @@ -761,18 +753,23 @@ class LookupRef $firstLower = StringHelper::strToLower($rowData[$firstColumn]); // break if we have passed possible keys - if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) || - (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))) { + if ( + (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) || + (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower)) + ) { break; } // remember the last key, but only if datatypes match - if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) || - (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))) { + if ( + (is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) || + (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn])) + ) { if ($not_exact_match) { $rowNumber = $rowKey; continue; - } elseif (($firstLower == $lookupLower) + } elseif ( + ($firstLower == $lookupLower) // Spreadsheets software returns first exact match, // we have sorted and we might have broken key orders // we want the first one (by its initial index) @@ -818,7 +815,7 @@ class LookupRef return Functions::REF(); } $f = array_keys($lookup_array); - $firstRow = array_pop($f); + $firstRow = reset($f); if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array))) { return Functions::REF(); } @@ -834,10 +831,12 @@ class LookupRef $lookupLower = StringHelper::strToLower($lookup_value); $rowDataLower = StringHelper::strToLower($rowData); - if ($not_exact_match && ( + if ( + $not_exact_match && ( ($bothNumeric && $rowData > $lookup_value) || ($bothNotNumeric && $rowDataLower > $lookupLower) - )) { + ) + ) { break; } @@ -847,7 +846,8 @@ class LookupRef $rowNumber = $rowKey; continue; - } elseif ($rowDataLower === $lookupLower + } elseif ( + $rowDataLower === $lookupLower && ($rowNumber === null || $rowKey < $rowNumber) ) { $rowNumber = $rowKey; @@ -945,7 +945,7 @@ class LookupRef * * @return string */ - public static function FORMULATEXT($cellReference = '', Cell $pCell = null) + public static function FORMULATEXT($cellReference = '', ?Cell $pCell = null) { if ($pCell === null) { return Functions::REF(); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php index fc9211b868e..823f6ef2c8d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; +use Exception; use Matrix\Exception as MatrixException; use Matrix\Matrix; @@ -38,6 +39,85 @@ class MathTrig return ($num - ($num % $n)) / $n; } + /** + * ARABIC. + * + * Converts a Roman numeral to an Arabic numeral. + * + * Excel Function: + * ARABIC(text) + * + * @param string $roman + * + * @return int|string the arabic numberal contrived from the roman numeral + */ + public static function ARABIC($roman) + { + // An empty string should return 0 + $roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255); + if ($roman === '') { + return 0; + } + + // Convert the roman numeral to an arabic number + $negativeNumber = $roman[0] === '-'; + if ($negativeNumber) { + $roman = substr($roman, 1); + } + + try { + $arabic = self::calculateArabic(str_split($roman)); + } catch (Exception $e) { + return Functions::VALUE(); // Invalid character detected + } + + if ($negativeNumber) { + $arabic *= -1; // The number should be negative + } + + return $arabic; + } + + /** + * Recursively calculate the arabic value of a roman numeral. + * + * @param int $sum + * @param int $subtract + * + * @return int + */ + protected static function calculateArabic(array $roman, &$sum = 0, $subtract = 0) + { + $lookup = [ + 'M' => 1000, + 'D' => 500, + 'C' => 100, + 'L' => 50, + 'X' => 10, + 'V' => 5, + 'I' => 1, + ]; + + $numeral = array_shift($roman); + if (!isset($lookup[$numeral])) { + throw new Exception('Invalid character detected'); + } + + $arabic = $lookup[$numeral]; + if (count($roman) > 0 && isset($lookup[$roman[0]]) && $arabic < $lookup[$roman[0]]) { + $subtract += $arabic; + } else { + $sum += ($arabic - $subtract); + $subtract = 0; + } + + if (count($roman) > 0) { + self::calculateArabic($roman, $sum, $subtract); + } + + return $sum; + } + /** * ATAN2. * @@ -54,12 +134,10 @@ class MathTrig * Excel Function: * ATAN2(xCoordinate,yCoordinate) * - * @category Mathematical and Trigonometric Functions - * * @param float $xCoordinate the x-coordinate of the point * @param float $yCoordinate the y-coordinate of the point * - * @return float the inverse tangent of the specified x- and y-coordinates + * @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error */ public static function ATAN2($xCoordinate = null, $yCoordinate = null) { @@ -69,8 +147,10 @@ class MathTrig $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0; $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0; - if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) && - ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) { + if ( + ((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) && + ((is_numeric($yCoordinate))) || (is_bool($yCoordinate)) + ) { $xCoordinate = (float) $xCoordinate; $yCoordinate = (float) $yCoordinate; @@ -84,6 +164,47 @@ class MathTrig return Functions::VALUE(); } + /** + * BASE. + * + * Converts a number into a text representation with the given radix (base). + * + * Excel Function: + * BASE(Number, Radix [Min_length]) + * + * @param float $number + * @param float $radix + * @param int $minLength + * + * @return string the text representation with the given radix (base) + */ + public static function BASE($number, $radix, $minLength = null) + { + $number = Functions::flattenSingleValue($number); + $radix = Functions::flattenSingleValue($radix); + $minLength = Functions::flattenSingleValue($minLength); + + if (is_numeric($number) && is_numeric($radix) && ($minLength === null || is_numeric($minLength))) { + // Truncate to an integer + $number = (int) $number; + $radix = (int) $radix; + $minLength = (int) $minLength; + + if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) { + return Functions::NAN(); // Numeric range constraints + } + + $outcome = strtoupper((string) base_convert($number, 10, $radix)); + if ($minLength !== null) { + $outcome = str_pad($outcome, $minLength, '0', STR_PAD_LEFT); // String padding + } + + return $outcome; + } + + return Functions::VALUE(); + } + /** * CEILING. * @@ -95,20 +216,20 @@ class MathTrig * Excel Function: * CEILING(number[,significance]) * - * @category Mathematical and Trigonometric Functions - * * @param float $number the number you want to round * @param float $significance the multiple to which you want to round * - * @return float Rounded Number + * @return float|string Rounded Number, or a string containing an error */ public static function CEILING($number, $significance = null) { $number = Functions::flattenSingleValue($number); $significance = Functions::flattenSingleValue($significance); - if (($significance === null) && - (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) { + if ( + ($significance === null) && + (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) + ) { $significance = $number / abs($number); } @@ -134,12 +255,10 @@ class MathTrig * Excel Function: * COMBIN(numObjs,numInSet) * - * @category Mathematical and Trigonometric Functions - * * @param int $numObjs Number of different objects * @param int $numInSet Number of objects in each combination * - * @return int Number of combinations + * @return int|string Number of combinations, or a string containing an error */ public static function COMBIN($numObjs, $numInSet) { @@ -171,11 +290,9 @@ class MathTrig * Excel Function: * EVEN(number) * - * @category Mathematical and Trigonometric Functions - * * @param float $number Number to round * - * @return int Rounded Number + * @return int|string Rounded Number, or a string containing an error */ public static function EVEN($number) { @@ -205,11 +322,9 @@ class MathTrig * Excel Function: * FACT(factVal) * - * @category Mathematical and Trigonometric Functions - * * @param float $factVal Factorial Value * - * @return int Factorial + * @return int|string Factorial, or a string containing an error */ public static function FACT($factVal) { @@ -220,8 +335,10 @@ class MathTrig return Functions::NAN(); } $factLoop = floor($factVal); - if ((Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) && - ($factVal > $factLoop)) { + if ( + (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) && + ($factVal > $factLoop) + ) { return Functions::NAN(); } @@ -244,11 +361,9 @@ class MathTrig * Excel Function: * FACTDOUBLE(factVal) * - * @category Mathematical and Trigonometric Functions - * * @param float $factVal Factorial Value * - * @return int Double Factorial + * @return int|string Double Factorial, or a string containing an error */ public static function FACTDOUBLE($factVal) { @@ -279,20 +394,20 @@ class MathTrig * Excel Function: * FLOOR(number[,significance]) * - * @category Mathematical and Trigonometric Functions - * * @param float $number Number to round * @param float $significance Significance * - * @return float Rounded Number + * @return float|string Rounded Number, or a string containing an error */ public static function FLOOR($number, $significance = null) { $number = Functions::flattenSingleValue($number); $significance = Functions::flattenSingleValue($significance); - if (($significance === null) && - (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) { + if ( + ($significance === null) && + (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) + ) { $significance = $number / abs($number); } @@ -313,6 +428,76 @@ class MathTrig return Functions::VALUE(); } + /** + * FLOOR.MATH. + * + * Round a number down to the nearest integer or to the nearest multiple of significance. + * + * Excel Function: + * FLOOR.MATH(number[,significance[,mode]]) + * + * @param float $number Number to round + * @param float $significance Significance + * @param int $mode direction to round negative numbers + * + * @return float|string Rounded Number, or a string containing an error + */ + public static function FLOORMATH($number, $significance = null, $mode = 0) + { + $number = Functions::flattenSingleValue($number); + $significance = Functions::flattenSingleValue($significance); + $mode = Functions::flattenSingleValue($mode); + + if (is_numeric($number) && $significance === null) { + $significance = $number / abs($number); + } + + if (is_numeric($number) && is_numeric($significance) && is_numeric($mode)) { + if ($significance == 0.0) { + return Functions::DIV0(); + } elseif ($number == 0.0) { + return 0.0; + } elseif (self::SIGN($significance) == -1 || (self::SIGN($number) == -1 && !empty($mode))) { + return ceil($number / $significance) * $significance; + } + + return floor($number / $significance) * $significance; + } + + return Functions::VALUE(); + } + + /** + * FLOOR.PRECISE. + * + * Rounds number down, toward zero, to the nearest multiple of significance. + * + * Excel Function: + * FLOOR.PRECISE(number[,significance]) + * + * @param float $number Number to round + * @param float $significance Significance + * + * @return float|string Rounded Number, or a string containing an error + */ + public static function FLOORPRECISE($number, $significance = 1) + { + $number = Functions::flattenSingleValue($number); + $significance = Functions::flattenSingleValue($significance); + + if ((is_numeric($number)) && (is_numeric($significance))) { + if ($significance == 0.0) { + return Functions::DIV0(); + } elseif ($number == 0.0) { + return 0.0; + } + + return floor($number / abs($significance)) * abs($significance); + } + + return Functions::VALUE(); + } + private static function evaluateGCD($a, $b) { return $b ? self::evaluateGCD($b, $a % $b) : $a; @@ -328,11 +513,9 @@ class MathTrig * Excel Function: * GCD(number1[,number2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * - * @return int Greatest Common Divisor + * @return int|mixed|string Greatest Common Divisor, or a string containing an error */ public static function GCD(...$args) { @@ -362,11 +545,9 @@ class MathTrig * Excel Function: * INT(number) * - * @category Mathematical and Trigonometric Functions - * * @param float $number Number to cast to an integer * - * @return int Integer value + * @return int|string Integer value, or a string containing an error */ public static function INT($number) { @@ -395,11 +576,9 @@ class MathTrig * Excel Function: * LCM(number1[,number2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * - * @return int Lowest Common Multiplier + * @return int|string Lowest Common Multiplier, or a string containing an error */ public static function LCM(...$args) { @@ -419,7 +598,7 @@ class MathTrig $myCountedFactors = array_count_values($myFactors); $myPoweredFactors = []; foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) { - $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower); + $myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower; } foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) { if (isset($allPoweredFactors[$myPoweredValue])) { @@ -446,12 +625,10 @@ class MathTrig * Excel Function: * LOG(number[,base]) * - * @category Mathematical and Trigonometric Functions - * * @param float $number The positive real number for which you want the logarithm * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10. * - * @return float + * @return float|string The result, or a string containing an error */ public static function logBase($number = null, $base = 10) { @@ -476,11 +653,9 @@ class MathTrig * Excel Function: * MDETERM(array) * - * @category Mathematical and Trigonometric Functions - * * @param array $matrixValues A matrix of values * - * @return float + * @return float|string The result, or a string containing an error */ public static function MDETERM($matrixValues) { @@ -528,11 +703,9 @@ class MathTrig * Excel Function: * MINVERSE(array) * - * @category Mathematical and Trigonometric Functions - * * @param array $matrixValues A matrix of values * - * @return array + * @return array|string The result, or a string containing an error */ public static function MINVERSE($matrixValues) { @@ -582,7 +755,7 @@ class MathTrig * @param array $matrixData1 A matrix of values * @param array $matrixData2 A matrix of values * - * @return array + * @return array|string The result, or a string containing an error */ public static function MMULT($matrixData1, $matrixData2) { @@ -644,7 +817,7 @@ class MathTrig * @param int $a Dividend * @param int $b Divisor * - * @return int Remainder + * @return int|string Remainder, or a string containing an error */ public static function MOD($a = 1, $b = 1) { @@ -670,7 +843,7 @@ class MathTrig * @param float $number Number to round * @param int $multiple Multiple to which you want to round $number * - * @return float Rounded Number + * @return float|string Rounded Number, or a string containing an error */ public static function MROUND($number, $multiple) { @@ -698,9 +871,9 @@ class MathTrig * * Returns the ratio of the factorial of a sum of values to the product of factorials. * - * @param array of mixed Data Series + * @param mixed[] $args An array of mixed values for the Data Series * - * @return float + * @return float|string The result, or a string containing an error */ public static function MULTINOMIAL(...$args) { @@ -737,7 +910,7 @@ class MathTrig * * @param float $number Number to round * - * @return int Rounded Number + * @return int|string Rounded Number, or a string containing an error */ public static function ODD($number) { @@ -772,7 +945,7 @@ class MathTrig * @param float $x * @param float $y * - * @return float + * @return float|string The result, or a string containing an error */ public static function POWER($x = 0, $y = 2) { @@ -787,7 +960,7 @@ class MathTrig } // Return - $result = pow($x, $y); + $result = $x ** $y; return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN(); } @@ -800,8 +973,6 @@ class MathTrig * Excel Function: * PRODUCT(value1[,value2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * * @return float @@ -840,8 +1011,6 @@ class MathTrig * Excel Function: * QUOTIENT(value1[,value2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * * @return float @@ -931,7 +1100,7 @@ class MathTrig * @param float $number Number to round * @param int $digits Number of digits to which you want to round $number * - * @return float Rounded Number + * @return float|string Rounded Number, or a string containing an error */ public static function ROUNDUP($number, $digits) { @@ -939,12 +1108,15 @@ class MathTrig $digits = Functions::flattenSingleValue($digits); if ((is_numeric($number)) && (is_numeric($digits))) { - $significance = pow(10, (int) $digits); - if ($number < 0.0) { - return floor($number * $significance) / $significance; + if ($number == 0.0) { + return 0.0; } - return ceil($number * $significance) / $significance; + if ($number < 0.0) { + return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN); + } + + return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN); } return Functions::VALUE(); @@ -958,7 +1130,7 @@ class MathTrig * @param float $number Number to round * @param int $digits Number of digits to which you want to round $number * - * @return float Rounded Number + * @return float|string Rounded Number, or a string containing an error */ public static function ROUNDDOWN($number, $digits) { @@ -966,12 +1138,15 @@ class MathTrig $digits = Functions::flattenSingleValue($digits); if ((is_numeric($number)) && (is_numeric($digits))) { - $significance = pow(10, (int) $digits); - if ($number < 0.0) { - return ceil($number * $significance) / $significance; + if ($number == 0.0) { + return 0.0; } - return floor($number * $significance) / $significance; + if ($number < 0.0) { + return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP); + } + + return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP); } return Functions::VALUE(); @@ -982,12 +1157,9 @@ class MathTrig * * Returns the sum of a power series * - * @param float $x Input value to the power series - * @param float $n Initial power to which you want to raise $x - * @param float $m Step by which to increase $n for each term in the series - * @param array of mixed Data Series + * @param mixed[] $args An array of mixed values for the Data Series * - * @return float + * @return float|string The result, or a string containing an error */ public static function SERIESSUM(...$args) { @@ -1006,7 +1178,7 @@ class MathTrig foreach ($aArgs as $arg) { // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { - $returnValue += $arg * pow($x, $n + ($m * $i++)); + $returnValue += $arg * $x ** ($n + ($m * $i++)); } else { return Functions::VALUE(); } @@ -1026,7 +1198,7 @@ class MathTrig * * @param float $number Number to round * - * @return int sign value + * @return int|string sign value, or a string containing an error */ public static function SIGN($number) { @@ -1053,7 +1225,7 @@ class MathTrig * * @param float $number Number * - * @return float Square Root of Number * Pi + * @return float|string Square Root of Number * Pi, or a string containing an error */ public static function SQRTPI($number) { @@ -1109,21 +1281,22 @@ class MathTrig * * Returns a subtotal in a list or database. * - * @param int the number 1 to 11 that specifies which function to + * @param int $functionType + * A number 1 to 11 that specifies which function to * use in calculating subtotals within a range * list * Numbers 101 to 111 shadow the functions of 1 to 11 * but ignore any values in the range that are * in hidden rows or columns - * @param array of mixed Data Series + * @param mixed[] $args A mixed data series of values * * @return float|string */ - public static function SUBTOTAL(...$args) + public static function SUBTOTAL($functionType, ...$args) { $cellReference = array_pop($args); $aArgs = Functions::flattenArrayIndexed($args); - $subtotal = array_shift($aArgs); + $subtotal = Functions::flattenSingleValue($functionType); // Calculate if ((is_numeric($subtotal)) && (!is_string($subtotal))) { @@ -1170,8 +1343,6 @@ class MathTrig * Excel Function: * SUM(value1[,value2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * * @return float @@ -1185,6 +1356,8 @@ class MathTrig // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { $returnValue += $arg; + } elseif (Functions::isError($arg)) { + return $arg; } } @@ -1199,8 +1372,6 @@ class MathTrig * Excel Function: * SUMIF(value1[,value2[, ...]],condition) * - * @category Mathematical and Trigonometric Functions - * * @param mixed $aArgs Data values * @param string $condition the criteria that defines which cells will be summed * @param mixed $sumArgs @@ -1227,8 +1398,10 @@ class MathTrig $testCondition = '=' . $arg . $condition; $sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0; - if (is_numeric($sumValue) && - Calculation::getInstance()->_calculateFormulaValue($testCondition)) { + if ( + is_numeric($sumValue) && + Calculation::getInstance()->_calculateFormulaValue($testCondition) + ) { // Is it a value within our criteria and only numeric can be added to the result $returnValue += $sumValue; } @@ -1245,10 +1418,7 @@ class MathTrig * Excel Function: * SUMIFS(value1[,value2[, ...]],condition) * - * @category Mathematical and Trigonometric Functions - * * @param mixed $args Data values - * @param string $condition the criteria that defines which cells will be summed * * @return float */ @@ -1303,11 +1473,9 @@ class MathTrig * Excel Function: * SUMPRODUCT(value1[,value2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string The result, or a string containing an error */ public static function SUMPRODUCT(...$args) { @@ -1348,8 +1516,6 @@ class MathTrig * Excel Function: * SUMSQ(value1[,value2[, ...]]) * - * @category Mathematical and Trigonometric Functions - * * @param mixed ...$args Data values * * @return float @@ -1385,8 +1551,10 @@ class MathTrig $result = 0; for ($i = 0; $i < $count; ++$i) { - if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && - ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) { + if ( + ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && + ((is_numeric($array2[$i])) && (!is_string($array2[$i]))) + ) { $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]); } } @@ -1410,8 +1578,10 @@ class MathTrig $result = 0; for ($i = 0; $i < $count; ++$i) { - if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && - ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) { + if ( + ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && + ((is_numeric($array2[$i])) && (!is_string($array2[$i]))) + ) { $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]); } } @@ -1435,8 +1605,10 @@ class MathTrig $result = 0; for ($i = 0; $i < $count; ++$i) { - if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && - ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) { + if ( + ((is_numeric($array1[$i])) && (!is_string($array1[$i]))) && + ((is_numeric($array2[$i])) && (!is_string($array2[$i]))) + ) { $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]); } } @@ -1452,7 +1624,7 @@ class MathTrig * @param float $value * @param int $digits * - * @return float Truncated value + * @return float|string Truncated value, or a string containing an error */ public static function TRUNC($value = 0, $digits = 0) { @@ -1466,7 +1638,7 @@ class MathTrig $digits = floor($digits); // Truncate - $adjust = pow(10, $digits); + $adjust = 10 ** $digits; if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) { return $value; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php index 1f9c76659ff..19f40f2d4a8 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php @@ -397,10 +397,10 @@ class Statistical for ($i = 1; $i <= $n; ++$i) { $divisor *= ($a + $i); } - $summer += (pow($x, $n) / $divisor); + $summer += ($x ** $n / $divisor); } - return pow($x, $a) * exp(0 - $x) * $summer; + return $x ** $a * exp(0 - $x) * $summer; } // @@ -530,9 +530,11 @@ class Statistical */ private static function testAcceptedBoolean($arg, $k) { - if ((is_bool($arg)) && + if ( + (is_bool($arg)) && ((!Functions::isCellValue($k) && (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_EXCEL)) || - (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE))) { + (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE)) + ) { $arg = (int) $arg; } @@ -547,9 +549,11 @@ class Statistical */ private static function isAcceptedCountable($arg, $k) { - if (((is_numeric($arg)) && (!is_string($arg))) || + if ( + ((is_numeric($arg)) && (!is_string($arg))) || ((is_numeric($arg)) && (!Functions::isCellValue($k)) && - (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))) { + (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC)) + ) { return true; } @@ -565,8 +569,6 @@ class Statistical * Excel Function: * AVEDEV(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -616,8 +618,6 @@ class Statistical * Excel Function: * AVERAGE(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -657,8 +657,6 @@ class Statistical * Excel Function: * AVERAGEA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -670,8 +668,10 @@ class Statistical $aCount = 0; // Loop through arguments foreach (Functions::flattenArrayIndexed($args) as $k => $arg) { - if ((is_bool($arg)) && - (!Functions::isMatrixValue($k))) { + if ( + (is_bool($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) { if (is_bool($arg)) { @@ -700,8 +700,6 @@ class Statistical * Excel Function: * AVERAGEIF(value1[,value2[, ...]],condition) * - * @category Mathematical and Trigonometric Functions - * * @param mixed $aArgs Data values * @param string $condition the criteria that defines which cells will be checked * @param mixed[] $averageArgs Data values @@ -787,7 +785,7 @@ class Statistical /** * BETAINV. * - * Returns the inverse of the beta distribution. + * Returns the inverse of the Beta distribution. * * @param float $probability Probability at which you want to evaluate the distribution * @param float $alpha Parameter to the distribution @@ -874,13 +872,13 @@ class Statistical if ($cumulative) { $summer = 0; for ($i = 0; $i <= $value; ++$i) { - $summer += MathTrig::COMBIN($trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i); + $summer += MathTrig::COMBIN($trials, $i) * $probability ** $i * (1 - $probability) ** ($trials - $i); } return $summer; } - return MathTrig::COMBIN($trials, $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value); + return MathTrig::COMBIN($trials, $value) * $probability ** $value * (1 - $probability) ** ($trials - $value); } } @@ -1053,8 +1051,6 @@ class Statistical * Excel Function: * COUNT(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return int @@ -1086,8 +1082,6 @@ class Statistical * Excel Function: * COUNTA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return int @@ -1116,8 +1110,6 @@ class Statistical * Excel Function: * COUNTBLANK(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return int @@ -1146,8 +1138,6 @@ class Statistical * Excel Function: * COUNTIF(value1[,value2[, ...]],condition) * - * @category Statistical Functions - * * @param mixed $aArgs Data values * @param string $condition the criteria that defines which cells will be counted * @@ -1188,8 +1178,6 @@ class Statistical * Excel Function: * COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…) * - * @category Statistical Functions - * * @param mixed $args Criterias * * @return int @@ -1295,7 +1283,7 @@ class Statistical * * @return int|string * - * @todo Warning. This implementation differs from the algorithm detailed on the MS + * @TODO Warning. This implementation differs from the algorithm detailed on the MS * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess * This eliminates a potential endless loop error, but may have an adverse affect on the * accuracy of the function (although all my tests have so far returned correct results). @@ -1320,7 +1308,7 @@ class Statistical $t = sqrt(log(1 / ($alpha * $alpha))); $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t)); } else { - $t = sqrt(log(1 / pow(1 - $alpha, 2))); + $t = sqrt(log(1 / (1 - $alpha) ** 2)); $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t); } @@ -1416,8 +1404,6 @@ class Statistical * Excel Function: * DEVSQ(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -1434,16 +1420,18 @@ class Statistical $aCount = -1; foreach ($aArgs as $k => $arg) { // Is it a numeric value? - if ((is_bool($arg)) && + if ( + (is_bool($arg)) && ((!Functions::isCellValue($k)) || - (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) { + (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) + ) { $arg = (int) $arg; } if ((is_numeric($arg)) && (!is_string($arg))) { if ($returnValue === null) { - $returnValue = pow(($arg - $aMean), 2); + $returnValue = ($arg - $aMean) ** 2; } else { - $returnValue += pow(($arg - $aMean), 2); + $returnValue += ($arg - $aMean) ** 2; } ++$aCount; } @@ -1457,7 +1445,7 @@ class Statistical return $returnValue; } - return self::NA(); + return Functions::NA(); } /** @@ -1495,6 +1483,62 @@ class Statistical return Functions::VALUE(); } + private static function betaFunction($a, $b) + { + return (self::gamma($a) * self::gamma($b)) / self::gamma($a + $b); + } + + private static function regularizedIncompleteBeta($value, $a, $b) + { + return self::incompleteBeta($value, $a, $b) / self::betaFunction($a, $b); + } + + /** + * F.DIST. + * + * Returns the F probability distribution. + * You can use this function to determine whether two data sets have different degrees of diversity. + * For example, you can examine the test scores of men and women entering high school, and determine + * if the variability in the females is different from that found in the males. + * + * @param float $value Value of the function + * @param int $u The numerator degrees of freedom + * @param int $v The denominator degrees of freedom + * @param bool $cumulative If cumulative is TRUE, F.DIST returns the cumulative distribution function; + * if FALSE, it returns the probability density function. + * + * @return float|string + */ + public static function FDIST2($value, $u, $v, $cumulative) + { + $value = Functions::flattenSingleValue($value); + $u = Functions::flattenSingleValue($u); + $v = Functions::flattenSingleValue($v); + $cumulative = Functions::flattenSingleValue($cumulative); + + if (is_numeric($value) && is_numeric($u) && is_numeric($v)) { + if ($value < 0 || $u < 1 || $v < 1) { + return Functions::NAN(); + } + + $cumulative = (bool) $cumulative; + $u = (int) $u; + $v = (int) $v; + + if ($cumulative) { + $adjustedValue = ($u * $value) / ($u * $value + $v); + + return self::incompleteBeta($adjustedValue, $u / 2, $v / 2); + } + + return (self::gamma(($v + $u) / 2) / (self::gamma($u / 2) * self::gamma($v / 2))) * + (($u / $v) ** ($u / 2)) * + (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2))); + } + + return Functions::VALUE(); + } + /** * FISHER. * @@ -1576,6 +1620,27 @@ class Statistical return $bestFitLinear->getValueOfYForX($xValue); } + /** + * GAMMA. + * + * Return the gamma function value. + * + * @param float $value + * + * @return float|string The result, or a string containing an error + */ + public static function GAMMAFunction($value) + { + $value = Functions::flattenSingleValue($value); + if (!is_numeric($value)) { + return Functions::VALUE(); + } elseif ((((int) $value) == ((float) $value)) && $value <= 0.0) { + return Functions::NAN(); + } + + return self::gamma($value); + } + /** * GAMMADIST. * @@ -1603,7 +1668,7 @@ class Statistical return self::incompleteGamma($a, $value / $b) / self::gamma($a); } - return (1 / (pow($b, $a) * self::gamma($a))) * pow($value, $a - 1) * exp(0 - ($value / $b)); + return (1 / ($b ** $a * self::gamma($a))) * $value ** ($a - 1) * exp(0 - ($value / $b)); } } @@ -1613,7 +1678,7 @@ class Statistical /** * GAMMAINV. * - * Returns the inverse of the beta distribution. + * Returns the inverse of the Gamma distribution. * * @param float $probability Probability at which you want to evaluate the distribution * @param float $alpha Parameter to the distribution @@ -1636,7 +1701,6 @@ class Statistical $xHi = $alpha * $beta * 5; $x = $xNew = 1; - $error = $pdf = 0; $dx = 1024; $i = 0; @@ -1697,6 +1761,26 @@ class Statistical return Functions::VALUE(); } + /** + * GAUSS. + * + * Calculates the probability that a member of a standard normal population will fall between + * the mean and z standard deviations from the mean. + * + * @param float $value + * + * @return float|string The result, or a string containing an error + */ + public static function GAUSS($value) + { + $value = Functions::flattenSingleValue($value); + if (!is_numeric($value)) { + return Functions::VALUE(); + } + + return self::NORMDIST($value, 0, 1, true) - 0.5; + } + /** * GEOMEAN. * @@ -1707,8 +1791,6 @@ class Statistical * Excel Function: * GEOMEAN(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -1721,7 +1803,7 @@ class Statistical if (is_numeric($aMean) && ($aMean > 0)) { $aCount = self::COUNT($aArgs); if (self::MIN($aArgs) > 0) { - return pow($aMean, (1 / $aCount)); + return $aMean ** (1 / $aCount); } } @@ -1769,8 +1851,6 @@ class Statistical * Excel Function: * HARMEAN(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -1820,12 +1900,17 @@ class Statistical */ public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) { - $sampleSuccesses = floor(Functions::flattenSingleValue($sampleSuccesses)); - $sampleNumber = floor(Functions::flattenSingleValue($sampleNumber)); - $populationSuccesses = floor(Functions::flattenSingleValue($populationSuccesses)); - $populationNumber = floor(Functions::flattenSingleValue($populationNumber)); + $sampleSuccesses = Functions::flattenSingleValue($sampleSuccesses); + $sampleNumber = Functions::flattenSingleValue($sampleNumber); + $populationSuccesses = Functions::flattenSingleValue($populationSuccesses); + $populationNumber = Functions::flattenSingleValue($populationNumber); if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) { + $sampleSuccesses = floor($sampleSuccesses); + $sampleNumber = floor($sampleNumber); + $populationSuccesses = floor($populationSuccesses); + $populationNumber = floor($populationNumber); + if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) { return Functions::NAN(); } @@ -1895,12 +1980,14 @@ class Statistical $count = $summer = 0; // Loop through arguments foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - (!Functions::isMatrixValue($k))) { + if ( + (is_bool($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { - $summer += pow((($arg - $mean) / $stdDev), 4); + $summer += (($arg - $mean) / $stdDev) ** 4; ++$count; } } @@ -1908,7 +1995,7 @@ class Statistical // Return if ($count > 3) { - return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * pow($count - 1, 2) / (($count - 2) * ($count - 3))); + return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 / (($count - 2) * ($count - 3))); } } @@ -1924,21 +2011,19 @@ class Statistical * Excel Function: * LARGE(value1[,value2[, ...]],entry) * - * @category Statistical Functions - * * @param mixed $args Data values - * @param int $entry Position (ordered from the largest) in the array or range of data to return * - * @return float + * @return float|string The result, or a string containing an error */ public static function LARGE(...$args) { $aArgs = Functions::flattenArray($args); - - // Calculate - $entry = floor(array_pop($aArgs)); + $entry = array_pop($aArgs); if ((is_numeric($entry)) && (!is_string($entry))) { + $entry = (int) floor($entry); + + // Calculate $mArgs = []; foreach ($aArgs as $arg) { // Is it a numeric value? @@ -1947,7 +2032,7 @@ class Statistical } } $count = self::COUNT($mArgs); - $entry = floor(--$entry); + --$entry; if (($entry < 0) || ($entry >= $count) || ($count == 0)) { return Functions::NAN(); } @@ -1970,7 +2055,7 @@ class Statistical * @param bool $const a logical value specifying whether to force the intersect to equal 0 * @param bool $stats a logical value specifying whether to return additional regression statistics * - * @return array + * @return array|int|string The result, or a string containing an error */ public static function LINEST($yValues, $xValues = null, $const = true, $stats = false) { @@ -2013,9 +2098,9 @@ class Statistical } return [ - $bestFitLinear->getSlope(), - $bestFitLinear->getIntersect(), - ]; + $bestFitLinear->getSlope(), + $bestFitLinear->getIntersect(), + ]; } /** @@ -2029,7 +2114,7 @@ class Statistical * @param bool $const a logical value specifying whether to force the intersect to equal 0 * @param bool $stats a logical value specifying whether to return additional regression statistics * - * @return array + * @return array|int|string The result, or a string containing an error */ public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false) { @@ -2078,9 +2163,9 @@ class Statistical } return [ - $bestFitExponential->getSlope(), - $bestFitExponential->getIntersect(), - ]; + $bestFitExponential->getSlope(), + $bestFitExponential->getIntersect(), + ]; } /** @@ -2092,9 +2177,9 @@ class Statistical * @param float $mean * @param float $stdDev * - * @return float + * @return float|string The result, or a string containing an error * - * @todo Try implementing P J Acklam's refinement algorithm for greater + * @TODO Try implementing P J Acklam's refinement algorithm for greater * accuracy if I can get my head round the mathematics * (as described at) http://home.online.no/~pjacklam/notes/invnorm/ */ @@ -2125,7 +2210,7 @@ class Statistical * @param float $mean * @param float $stdDev * - * @return float + * @return float|string The result, or a string containing an error */ public static function LOGNORMDIST($value, $mean, $stdDev) { @@ -2144,6 +2229,42 @@ class Statistical return Functions::VALUE(); } + /** + * LOGNORM.DIST. + * + * Returns the lognormal distribution of x, where ln(x) is normally distributed + * with parameters mean and standard_dev. + * + * @param float $value + * @param float $mean + * @param float $stdDev + * @param bool $cumulative + * + * @return float|string The result, or a string containing an error + */ + public static function LOGNORMDIST2($value, $mean, $stdDev, $cumulative = false) + { + $value = Functions::flattenSingleValue($value); + $mean = Functions::flattenSingleValue($mean); + $stdDev = Functions::flattenSingleValue($stdDev); + $cumulative = (bool) Functions::flattenSingleValue($cumulative); + + if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) { + if (($value <= 0) || ($stdDev <= 0)) { + return Functions::NAN(); + } + + if ($cumulative === true) { + return self::NORMSDIST2((log($value) - $mean) / $stdDev, true); + } + + return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) * + exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2))); + } + + return Functions::VALUE(); + } + /** * MAX. * @@ -2153,8 +2274,6 @@ class Statistical * Excel Function: * MAX(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float @@ -2189,8 +2308,6 @@ class Statistical * Excel Function: * MAXA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float @@ -2230,8 +2347,6 @@ class Statistical * Excel Function: * MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...) * - * @category Statistical Functions - * * @param mixed $args Data range and criterias * * @return float @@ -2289,11 +2404,9 @@ class Statistical * Excel Function: * MEDIAN(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string The result, or a string containing an error */ public static function MEDIAN(...$args) { @@ -2333,8 +2446,6 @@ class Statistical * Excel Function: * MIN(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float @@ -2369,8 +2480,6 @@ class Statistical * Excel Function: * MINA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float @@ -2410,8 +2519,6 @@ class Statistical * Excel Function: * MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...) * - * @category Statistical Functions - * * @param mixed $args Data range and criterias * * @return float @@ -2468,11 +2575,27 @@ class Statistical private static function modeCalc($data) { $frequencyArray = []; + $index = 0; + $maxfreq = 0; + $maxfreqkey = ''; + $maxfreqdatum = ''; foreach ($data as $datum) { $found = false; + ++$index; foreach ($frequencyArray as $key => $value) { if ((string) $value['value'] == (string) $datum) { ++$frequencyArray[$key]['frequency']; + $freq = $frequencyArray[$key]['frequency']; + if ($freq > $maxfreq) { + $maxfreq = $freq; + $maxfreqkey = $key; + $maxfreqdatum = $datum; + } elseif ($freq == $maxfreq) { + if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) { + $maxfreqkey = $key; + $maxfreqdatum = $datum; + } + } $found = true; break; @@ -2482,21 +2605,16 @@ class Statistical $frequencyArray[] = [ 'value' => $datum, 'frequency' => 1, + 'index' => $index, ]; } } - foreach ($frequencyArray as $key => $value) { - $frequencyList[$key] = $value['frequency']; - $valueList[$key] = $value['value']; - } - array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray); - - if ($frequencyArray[0]['frequency'] == 1) { + if ($maxfreq <= 1) { return Functions::NA(); } - return $frequencyArray[0]['value']; + return $maxfreqdatum; } /** @@ -2507,11 +2625,9 @@ class Statistical * Excel Function: * MODE(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string The result, or a string containing an error */ public static function MODE(...$args) { @@ -2548,7 +2664,7 @@ class Statistical * @param float $successes Threshold number of Successes * @param float $probability Probability of success on each trial * - * @return float + * @return float|string The result, or a string containing an error */ public static function NEGBINOMDIST($failures, $successes, $probability) { @@ -2568,7 +2684,7 @@ class Statistical } } - return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures)); + return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * ($probability ** $successes) * ((1 - $probability) ** $failures); } return Functions::VALUE(); @@ -2586,7 +2702,7 @@ class Statistical * @param float $stdDev Standard Deviation * @param bool $cumulative * - * @return float + * @return float|string The result, or a string containing an error */ public static function NORMDIST($value, $mean, $stdDev, $cumulative) { @@ -2603,7 +2719,7 @@ class Statistical return 0.5 * (1 + Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2)))); } - return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean, 2) / (2 * ($stdDev * $stdDev)))); + return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev)))); } } @@ -2619,7 +2735,7 @@ class Statistical * @param float $mean Mean Value * @param float $stdDev Standard Deviation * - * @return float + * @return float|string The result, or a string containing an error */ public static function NORMINV($probability, $mean, $stdDev) { @@ -2650,15 +2766,41 @@ class Statistical * * @param float $value * - * @return float + * @return float|string The result, or a string containing an error */ public static function NORMSDIST($value) { $value = Functions::flattenSingleValue($value); + if (!is_numeric($value)) { + return Functions::VALUE(); + } return self::NORMDIST($value, 0, 1, true); } + /** + * NORM.S.DIST. + * + * Returns the standard normal cumulative distribution function. The distribution has + * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a + * table of standard normal curve areas. + * + * @param float $value + * @param bool $cumulative + * + * @return float|string The result, or a string containing an error + */ + public static function NORMSDIST2($value, $cumulative) + { + $value = Functions::flattenSingleValue($value); + if (!is_numeric($value)) { + return Functions::VALUE(); + } + $cumulative = (bool) Functions::flattenSingleValue($cumulative); + + return self::NORMDIST($value, 0, 1, $cumulative); + } + /** * NORMSINV. * @@ -2666,7 +2808,7 @@ class Statistical * * @param float $value * - * @return float + * @return float|string The result, or a string containing an error */ public static function NORMSINV($value) { @@ -2681,12 +2823,9 @@ class Statistical * Excel Function: * PERCENTILE(value1[,value2[, ...]],entry) * - * @category Statistical Functions - * * @param mixed $args Data values - * @param float $entry Percentile value in the range 0..1, inclusive. * - * @return float + * @return float|string The result, or a string containing an error */ public static function PERCENTILE(...$args) { @@ -2734,7 +2873,7 @@ class Statistical * @param int $value the number whose rank you want to find * @param int $significance the number of significant digits for the returned percentage value * - * @return float + * @return float|string (string if result is an error) */ public static function PERCENTRANK($valueSet, $value, $significance = 3) { @@ -2784,7 +2923,7 @@ class Statistical * @param int $numObjs Number of different objects * @param int $numInSet Number of objects in each permutation * - * @return int|string Number of permutations + * @return int|string Number of permutations, or a string containing an error */ public static function PERMUT($numObjs, $numInSet) { @@ -2814,7 +2953,7 @@ class Statistical * @param float $mean Mean Value * @param bool $cumulative * - * @return float + * @return float|string The result, or a string containing an error */ public static function POISSON($value, $mean, $cumulative) { @@ -2830,13 +2969,13 @@ class Statistical $summer = 0; $floor = floor($value); for ($i = 0; $i <= $floor; ++$i) { - $summer += pow($mean, $i) / MathTrig::FACT($i); + $summer += $mean ** $i / MathTrig::FACT($i); } return exp(0 - $mean) * $summer; } - return (exp(0 - $mean) * pow($mean, $value)) / MathTrig::FACT($value); + return (exp(0 - $mean) * $mean ** $value) / MathTrig::FACT($value); } } @@ -2851,12 +2990,9 @@ class Statistical * Excel Function: * QUARTILE(value1[,value2[, ...]],entry) * - * @category Statistical Functions - * * @param mixed $args Data values - * @param int $entry Quartile value in the range 1..3, inclusive. * - * @return float + * @return float|string The result, or a string containing an error */ public static function QUARTILE(...$args) { @@ -2886,7 +3022,7 @@ class Statistical * @param float[] $valueSet An array of, or a reference to, a list of numbers * @param int $order Order to sort the values in the value set * - * @return float + * @return float|string The result, or a string containing an error */ public static function RANK($value, $valueSet, $order = 0) { @@ -2921,7 +3057,7 @@ class Statistical * @param mixed[] $yValues Data Series Y * @param mixed[] $xValues Data Series X * - * @return float|string + * @return float|string The result, or a string containing an error */ public static function RSQ($yValues, $xValues) { @@ -2952,7 +3088,7 @@ class Statistical * * @param array ...$args Data Series * - * @return float|string + * @return float|string The result, or a string containing an error */ public static function SKEW(...$args) { @@ -2963,12 +3099,14 @@ class Statistical $count = $summer = 0; // Loop through arguments foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - (!Functions::isMatrixValue($k))) { + if ( + (is_bool($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { - $summer += pow((($arg - $mean) / $stdDev), 3); + $summer += (($arg - $mean) / $stdDev) ** 3; ++$count; } } @@ -2989,7 +3127,7 @@ class Statistical * @param mixed[] $yValues Data Series Y * @param mixed[] $xValues Data Series X * - * @return float|string + * @return float|string The result, or a string containing an error */ public static function SLOPE($yValues, $xValues) { @@ -3019,12 +3157,9 @@ class Statistical * Excel Function: * SMALL(value1[,value2[, ...]],entry) * - * @category Statistical Functions - * * @param mixed $args Data values - * @param int $entry Position (ordered from the smallest) in the array or range of data to return * - * @return float + * @return float|string The result, or a string containing an error */ public static function SMALL(...$args) { @@ -3034,6 +3169,8 @@ class Statistical $entry = array_pop($aArgs); if ((is_numeric($entry)) && (!is_string($entry))) { + $entry = (int) floor($entry); + $mArgs = []; foreach ($aArgs as $arg) { // Is it a numeric value? @@ -3042,7 +3179,7 @@ class Statistical } } $count = self::COUNT($mArgs); - $entry = floor(--$entry); + --$entry; if (($entry < 0) || ($entry >= $count) || ($count == 0)) { return Functions::NAN(); } @@ -3063,7 +3200,7 @@ class Statistical * @param float $mean Mean Value * @param float $stdDev Standard Deviation * - * @return float Standardized value + * @return float|string Standardized value, or a string containing an error */ public static function STANDARDIZE($value, $mean, $stdDev) { @@ -3091,11 +3228,9 @@ class Statistical * Excel Function: * STDEV(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float|string + * @return float|string The result, or a string containing an error */ public static function STDEV(...$args) { @@ -3108,16 +3243,18 @@ class Statistical if ($aMean !== null) { $aCount = -1; foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) { + if ( + (is_bool($arg)) && + ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) + ) { $arg = (int) $arg; } // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { if ($returnValue === null) { - $returnValue = pow(($arg - $aMean), 2); + $returnValue = ($arg - $aMean) ** 2; } else { - $returnValue += pow(($arg - $aMean), 2); + $returnValue += ($arg - $aMean) ** 2; } ++$aCount; } @@ -3140,8 +3277,6 @@ class Statistical * Excel Function: * STDEVA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -3156,8 +3291,10 @@ class Statistical if ($aMean !== null) { $aCount = -1; foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - (!Functions::isMatrixValue($k))) { + if ( + (is_bool($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) { @@ -3167,9 +3304,9 @@ class Statistical $arg = 0; } if ($returnValue === null) { - $returnValue = pow(($arg - $aMean), 2); + $returnValue = ($arg - $aMean) ** 2; } else { - $returnValue += pow(($arg - $aMean), 2); + $returnValue += ($arg - $aMean) ** 2; } ++$aCount; } @@ -3192,8 +3329,6 @@ class Statistical * Excel Function: * STDEVP(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -3208,16 +3343,18 @@ class Statistical if ($aMean !== null) { $aCount = 0; foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) { + if ( + (is_bool($arg)) && + ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) + ) { $arg = (int) $arg; } // Is it a numeric value? if ((is_numeric($arg)) && (!is_string($arg))) { if ($returnValue === null) { - $returnValue = pow(($arg - $aMean), 2); + $returnValue = ($arg - $aMean) ** 2; } else { - $returnValue += pow(($arg - $aMean), 2); + $returnValue += ($arg - $aMean) ** 2; } ++$aCount; } @@ -3239,8 +3376,6 @@ class Statistical * Excel Function: * STDEVPA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * * @return float|string @@ -3255,8 +3390,10 @@ class Statistical if ($aMean !== null) { $aCount = 0; foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && - (!Functions::isMatrixValue($k))) { + if ( + (is_bool($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) { @@ -3266,9 +3403,9 @@ class Statistical $arg = 0; } if ($returnValue === null) { - $returnValue = pow(($arg - $aMean), 2); + $returnValue = ($arg - $aMean) ** 2; } else { - $returnValue += pow(($arg - $aMean), 2); + $returnValue += ($arg - $aMean) ** 2; } ++$aCount; } @@ -3321,7 +3458,7 @@ class Statistical * @param float $degrees degrees of freedom * @param float $tails number of tails (1 or 2) * - * @return float + * @return float|string The result, or a string containing an error */ public static function TDIST($value, $degrees, $tails) { @@ -3346,7 +3483,6 @@ class Statistical $ttheta = atan2($value, sqrt($tterm)); $tc = cos($ttheta); $ts = sin($ttheta); - $tsum = 0; if (($degrees % 2) == 1) { $ti = 3; @@ -3385,7 +3521,7 @@ class Statistical * @param float $probability Probability for the function * @param float $degrees degrees of freedom * - * @return float + * @return float|string The result, or a string containing an error */ public static function TINV($probability, $degrees) { @@ -3477,10 +3613,7 @@ class Statistical * Excel Function: * TRIMEAN(value1[,value2[, ...]], $discard) * - * @category Statistical Functions - * * @param mixed $args Data values - * @param float $discard Percentage to discard * * @return float|string */ @@ -3523,11 +3656,9 @@ class Statistical * Excel Function: * VAR(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string (string if result is an error) */ public static function VARFunc(...$args) { @@ -3567,11 +3698,9 @@ class Statistical * Excel Function: * VARA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string (string if result is an error) */ public static function VARA(...$args) { @@ -3583,11 +3712,15 @@ class Statistical $aArgs = Functions::flattenArrayIndexed($args); $aCount = 0; foreach ($aArgs as $k => $arg) { - if ((is_string($arg)) && - (Functions::isValue($k))) { + if ( + (is_string($arg)) && + (Functions::isValue($k)) + ) { return Functions::VALUE(); - } elseif ((is_string($arg)) && - (!Functions::isMatrixValue($k))) { + } elseif ( + (is_string($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) { @@ -3620,11 +3753,9 @@ class Statistical * Excel Function: * VARP(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string (string if result is an error) */ public static function VARP(...$args) { @@ -3665,11 +3796,9 @@ class Statistical * Excel Function: * VARPA(value1[,value2[, ...]]) * - * @category Statistical Functions - * * @param mixed ...$args Data values * - * @return float + * @return float|string (string if result is an error) */ public static function VARPA(...$args) { @@ -3681,11 +3810,15 @@ class Statistical $aArgs = Functions::flattenArrayIndexed($args); $aCount = 0; foreach ($aArgs as $k => $arg) { - if ((is_string($arg)) && - (Functions::isValue($k))) { + if ( + (is_string($arg)) && + (Functions::isValue($k)) + ) { return Functions::VALUE(); - } elseif ((is_string($arg)) && - (!Functions::isMatrixValue($k))) { + } elseif ( + (is_string($arg)) && + (!Functions::isMatrixValue($k)) + ) { } else { // Is it a numeric value? if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) { @@ -3721,7 +3854,7 @@ class Statistical * @param float $beta Beta Parameter * @param bool $cumulative * - * @return float + * @return float|string (string if result is an error) */ public static function WEIBULL($value, $alpha, $beta, $cumulative) { @@ -3735,10 +3868,10 @@ class Statistical } if ((is_numeric($cumulative)) || (is_bool($cumulative))) { if ($cumulative) { - return 1 - exp(0 - pow($value / $beta, $alpha)); + return 1 - exp(0 - ($value / $beta) ** $alpha); } - return ($alpha / pow($beta, $alpha)) * pow($value, $alpha - 1) * exp(0 - pow($value / $beta, $alpha)); + return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha); } } @@ -3755,7 +3888,7 @@ class Statistical * @param float $m0 Alpha Parameter * @param float $sigma Beta Parameter * - * @return float|string + * @return float|string (string if result is an error) */ public static function ZTEST($dataSet, $m0, $sigma = null) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php index bbb03926ca4..f89744029e7 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; +use DateTimeInterface; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; @@ -26,15 +27,15 @@ class TextData { $character = Functions::flattenSingleValue($character); - if ((!is_numeric($character)) || ($character < 0)) { + if (!is_numeric($character)) { + return Functions::VALUE(); + } + $character = (int) $character; + if ($character < 1 || $character > 255) { return Functions::VALUE(); } - if (function_exists('iconv')) { - return iconv('UCS-4LE', 'UTF-8', pack('V', $character)); - } - - return mb_convert_encoding('&#' . (int) $character . ';', 'UTF-8', 'HTML-ENTITIES'); + return iconv('UCS-4LE', 'UTF-8', pack('V', $character)); } /** @@ -98,7 +99,7 @@ class TextData * * @param string $characters Value * - * @return int + * @return int|string A string if arguments are invalid */ public static function ASCIICODE($characters) { @@ -159,7 +160,7 @@ class TextData // Validate parameters if (!is_numeric($value) || !is_numeric($decimals)) { - return Functions::NAN(); + return Functions::VALUE(); } $decimals = floor($decimals); @@ -167,12 +168,13 @@ class TextData if ($decimals > 0) { $mask .= '.' . str_repeat('0', $decimals); } else { - $round = pow(10, abs($decimals)); + $round = 10 ** abs($decimals); if ($value < 0) { $round = 0 - $round; } $value = MathTrig::MROUND($value, $round); } + $mask = "$mask;($mask)"; return NumberFormat::toFormattedString($value, $mask); } @@ -264,7 +266,7 @@ class TextData // Validate parameters if (!is_numeric($value) || !is_numeric($decimals)) { - return Functions::NAN(); + return Functions::VALUE(); } $decimals = (int) floor($decimals); @@ -543,7 +545,7 @@ class TextData * * @param mixed $value Value to check * - * @return bool + * @return DateTimeInterface|float|int|string A string if arguments are invalid */ public static function VALUE($value = '') { @@ -623,7 +625,7 @@ class TextData $percentageAdjustment = strlen($value) - strlen($percentageString); if ($percentageAdjustment) { $value = (float) $percentageString; - $value /= pow(10, $percentageAdjustment * 2); + $value /= 10 ** ($percentageAdjustment * 2); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php index 341a017979c..941e1ad7649 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php @@ -49,7 +49,7 @@ class Stack $storeKey = null, $onlyIf = null, $onlyIfNot = null - ) { + ): void { $stackItem = $this->getStackItem($type, $value, $reference, $storeKey, $onlyIf, $onlyIfNot); $this->stack[$this->count++] = $stackItem; @@ -124,7 +124,7 @@ class Stack /** * Clear the stack. */ - public function clear() + public function clear(): void { $this->stack = []; $this->count = 0; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php new file mode 100644 index 00000000000..5cfd2ea8433 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php @@ -0,0 +1,53 @@ + 2048) { + return Functions::VALUE(); // Invalid URL length + } + + if (!preg_match('/^http[s]?:\/\//', $url)) { + return Functions::VALUE(); // Invalid protocol + } + + // Get results from the the webservice + $client = Settings::getHttpClient(); + $requestFactory = Settings::getRequestFactory(); + $request = $requestFactory->createRequest('GET', $url); + + try { + $response = $client->sendRequest($request); + } catch (ClientExceptionInterface $e) { + return Functions::VALUE(); // cURL error + } + + if ($response->getStatusCode() != 200) { + return Functions::VALUE(); // cURL error + } + + $output = $response->getBody()->getContents(); + if (strlen($output) > 32767) { + return Functions::VALUE(); // Output not a string or too long + } + + return $output; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt index 97a0ceeb694..e71d18f4541 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt @@ -9,6 +9,7 @@ ADDRESS AMORDEGRC AMORLINC AND +ARABIC AREAS ASC ASIN @@ -22,6 +23,7 @@ AVERAGEA AVERAGEIF AVERAGEIFS BAHTTEXT +BASE BESSELI BESSELJ BESSELK @@ -137,6 +139,8 @@ FISHER FISHERINV FIXED FLOOR +FLOOR.MATH +FLOOR.PRECISE FORECAST FREQUENCY FTEST @@ -159,6 +163,7 @@ HYPERLINK HYPGEOMDIST IF IFERROR +IFS IMABS IMAGINARY IMARGUMENT @@ -312,6 +317,8 @@ SEC SECH SECOND SERIESSUM +SHEET +SHEETS SIGN SIN SINH diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php new file mode 100644 index 00000000000..04fa3b8ceec --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php @@ -0,0 +1,134 @@ +parent = null; } - public function attach(Cells $parent) + public function attach(Cells $parent): void { $this->parent = $parent; } @@ -91,9 +91,6 @@ class Cell * * @param mixed $pValue * @param string $pDataType - * @param Worksheet $pSheet - * - * @throws Exception */ public function __construct($pValue, $pDataType, Worksheet $pSheet) { @@ -175,9 +172,7 @@ class Cell * * @param mixed $pValue Value * - * @throws Exception - * - * @return Cell + * @return $this */ public function setValue($pValue) { @@ -194,8 +189,6 @@ class Cell * @param mixed $pValue Value * @param string $pDataType Explicit data type, see DataType::TYPE_* * - * @throws Exception - * * @return Cell */ public function setValueExplicit($pValue, $pDataType) @@ -217,6 +210,9 @@ class Cell break; case DataType::TYPE_NUMERIC: + if (is_string($pValue) && !is_numeric($pValue)) { + throw new Exception('Invalid numeric value for datatype Numeric'); + } $this->value = 0 + $pValue; break; @@ -249,26 +245,28 @@ class Cell * * @param bool $resetLog Whether the calculation engine logger should be reset or not * - * @throws Exception - * * @return mixed */ public function getCalculatedValue($resetLog = true) { if ($this->dataType == DataType::TYPE_FORMULA) { try { + $index = $this->getWorksheet()->getParent()->getActiveSheetIndex(); $result = Calculation::getInstance( $this->getWorksheet()->getParent() )->calculateCellValue($this, $resetLog); + $this->getWorksheet()->getParent()->setActiveSheetIndex($index); // We don't yet handle array returns if (is_array($result)) { while (is_array($result)) { - $result = array_pop($result); + $result = array_shift($result); } } } catch (Exception $ex) { if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) { return $this->calculatedValue; // Fallback for calculations referencing external files. + } elseif (strpos($ex->getMessage(), 'undefined name') !== false) { + return \PhpOffice\PhpSpreadsheet\Calculation\Functions::NAME(); } throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception( @@ -359,8 +357,6 @@ class Cell /** * Does this cell contain Data validation rules? * - * @throws Exception - * * @return bool */ public function hasDataValidation() @@ -375,8 +371,6 @@ class Cell /** * Get Data validation rules. * - * @throws Exception - * * @return DataValidation */ public function getDataValidation() @@ -393,11 +387,9 @@ class Cell * * @param DataValidation $pDataValidation * - * @throws Exception - * * @return Cell */ - public function setDataValidation(DataValidation $pDataValidation = null) + public function setDataValidation(?DataValidation $pDataValidation = null) { if (!isset($this->parent)) { throw new Exception('Cannot set data validation for cell that is not bound to a worksheet'); @@ -423,8 +415,6 @@ class Cell /** * Does this cell contain a Hyperlink? * - * @throws Exception - * * @return bool */ public function hasHyperlink() @@ -439,8 +429,6 @@ class Cell /** * Get Hyperlink. * - * @throws Exception - * * @return Hyperlink */ public function getHyperlink() @@ -457,11 +445,9 @@ class Cell * * @param Hyperlink $pHyperlink * - * @throws Exception - * * @return Cell */ - public function setHyperlink(Hyperlink $pHyperlink = null) + public function setHyperlink(?Hyperlink $pHyperlink = null) { if (!isset($this->parent)) { throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet'); @@ -549,8 +535,6 @@ class Cell /** * Re-bind parent. * - * @param Worksheet $parent - * * @return Cell */ public function rebindParent(Worksheet $parent) @@ -617,10 +601,8 @@ class Cell /** * Set value binder to use. - * - * @param IValueBinder $binder */ - public static function setValueBinder(IValueBinder $binder) + public static function setValueBinder(IValueBinder $binder): void { self::$valueBinder = $binder; } @@ -669,7 +651,7 @@ class Cell * * @param mixed $pAttributes * - * @return Cell + * @return $this */ public function setFormulaAttributes($pAttributes) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php index cc0543f6ab4..2afeebe9dd9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php @@ -25,8 +25,6 @@ abstract class Coordinate * * @param string $pCoordinateString eg: 'A1' * - * @throws Exception - * * @return string[] Array containing column and row (indexes 0 and 1) */ public static function coordinateFromString($pCoordinateString) @@ -60,8 +58,6 @@ abstract class Coordinate * @param string $pCoordinateString e.g. 'A' or '1' or 'A1' * Note that this value can be a row or column reference as well as a cell reference * - * @throws Exception - * * @return string Absolute coordinate e.g. '$A' or '$1' or '$A$1' */ public static function absoluteReference($pCoordinateString) @@ -91,8 +87,6 @@ abstract class Coordinate * * @param string $pCoordinateString e.g. 'A1' * - * @throws Exception - * * @return string Absolute coordinate e.g. '$A$1' */ public static function absoluteCoordinate($pCoordinateString) @@ -145,8 +139,6 @@ abstract class Coordinate * * @param array $pRange Array containg one or more arrays containing one or two coordinate strings * - * @throws Exception - * * @return string String representation of $pRange */ public static function buildRange(array $pRange) @@ -320,32 +312,59 @@ abstract class Coordinate /** * Extract all cell references in range, which may be comprised of multiple cell ranges. * - * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25) + * @param string $cellRange Range: e.g. 'A1' or 'A1:C10' or 'A1:E10,A20:E25' or 'A1:E5 C3:G7' or 'A1:C1,A3:C3 B1:C3' * * @return array Array containing single cell references */ - public static function extractAllCellReferencesInRange($pRange) + public static function extractAllCellReferencesInRange($cellRange): array { - $returnValue = []; + [$ranges, $operators] = self::getCellBlocksFromRangeString($cellRange); - // Explode spaces - $cellBlocks = self::getCellBlocksFromRangeString($pRange); - foreach ($cellBlocks as $cellBlock) { - $returnValue = array_merge($returnValue, self::getReferencesForCellBlock($cellBlock)); + $cells = []; + foreach ($ranges as $range) { + $cells[] = self::getReferencesForCellBlock($range); } + $cells = self::processRangeSetOperators($operators, $cells); + + if (empty($cells)) { + return []; + } + + $cellList = array_merge(...$cells); + $cellList = self::sortCellReferenceArray($cellList); + + return $cellList; + } + + private static function processRangeSetOperators(array $operators, array $cells): array + { + for ($offset = 0; $offset < count($operators); ++$offset) { + $operator = $operators[$offset]; + if ($operator !== ' ') { + continue; + } + + $cells[$offset] = array_intersect($cells[$offset], $cells[$offset + 1]); + unset($operators[$offset], $cells[$offset + 1]); + $operators = array_values($operators); + $cells = array_values($cells); + --$offset; + } + + return $cells; + } + + private static function sortCellReferenceArray(array $cellList): array + { // Sort the result by column and row $sortKeys = []; - foreach (array_unique($returnValue) as $coord) { - $column = ''; - $row = 0; - - sscanf($coord, '%[A-Z]%d', $column, $row); + foreach ($cellList as $coord) { + [$column, $row] = sscanf($coord, '%[A-Z]%d'); $sortKeys[sprintf('%3s%09d', $column, $row)] = $coord; } ksort($sortKeys); - // Return value return array_values($sortKeys); } @@ -490,15 +509,25 @@ abstract class Coordinate } /** - * Get the individual cell blocks from a range string, splitting by space and removing any $ characters. + * Get the individual cell blocks from a range string, removing any $ characters. + * then splitting by operators and returning an array with ranges and operators. * - * @param string $pRange + * @param string $rangeString * - * @return string[] + * @return array[] */ - private static function getCellBlocksFromRangeString($pRange) + private static function getCellBlocksFromRangeString($rangeString) { - return explode(' ', str_replace('$', '', strtoupper($pRange))); + $rangeString = str_replace('$', '', strtoupper($rangeString)); + + // split range sets on intersection (space) or union (,) operators + $tokens = preg_split('/([ ,])/', $rangeString, -1, PREG_SPLIT_DELIM_CAPTURE); + // separate the range sets and the operators into arrays + $split = array_chunk($tokens, 2); + $ranges = array_column($split, 0); + $operators = array_column($split, 1); + + return [$ranges, $operators]; } /** @@ -511,7 +540,7 @@ abstract class Coordinate * @param int $currentRow * @param int $endRow */ - private static function validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow) + private static function validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow): void { if ($startColumnIndex >= $endColumnIndex || $currentRow > $endRow) { throw new Exception('Invalid range: "' . $cellBlock . '"'); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php index a041ea0e0ab..dfeb024c928 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php @@ -142,7 +142,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setFormula1($value) { @@ -166,7 +166,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setFormula2($value) { @@ -190,7 +190,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setType($value) { @@ -214,7 +214,7 @@ class DataValidation * * @param string $value see self::STYLE_* * - * @return DataValidation + * @return $this */ public function setErrorStyle($value) { @@ -238,7 +238,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setOperator($value) { @@ -262,7 +262,7 @@ class DataValidation * * @param bool $value * - * @return DataValidation + * @return $this */ public function setAllowBlank($value) { @@ -286,7 +286,7 @@ class DataValidation * * @param bool $value * - * @return DataValidation + * @return $this */ public function setShowDropDown($value) { @@ -310,7 +310,7 @@ class DataValidation * * @param bool $value * - * @return DataValidation + * @return $this */ public function setShowInputMessage($value) { @@ -334,7 +334,7 @@ class DataValidation * * @param bool $value * - * @return DataValidation + * @return $this */ public function setShowErrorMessage($value) { @@ -358,7 +358,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setErrorTitle($value) { @@ -382,7 +382,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setError($value) { @@ -406,7 +406,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setPromptTitle($value) { @@ -430,7 +430,7 @@ class DataValidation * * @param string $value * - * @return DataValidation + * @return $this */ public function setPrompt($value) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php index cd05cf8bdf9..693446e698c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php @@ -14,8 +14,6 @@ class DefaultValueBinder implements IValueBinder * @param Cell $cell Cell to bind value to * @param mixed $value Value to bind in cell * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * * @return bool */ public function bindValue(Cell $cell, $value) @@ -67,6 +65,8 @@ class DefaultValueBinder implements IValueBinder return DataType::TYPE_STRING; } elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) { return DataType::TYPE_STRING; + } elseif (!is_numeric($pValue)) { + return DataType::TYPE_STRING; } return DataType::TYPE_NUMERIC; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php index e17c20d9be0..003d51014d9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php @@ -46,7 +46,7 @@ class Hyperlink * * @param string $value * - * @return Hyperlink + * @return $this */ public function setUrl($value) { @@ -70,7 +70,7 @@ class Hyperlink * * @param string $value * - * @return Hyperlink + * @return $this */ public function setTooltip($value) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php index 0552677f295..346d025347a 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php @@ -12,8 +12,6 @@ class StringValueBinder implements IValueBinder * @param Cell $cell Cell to bind value to * @param mixed $value Value to bind in cell * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * * @return bool */ public function bindValue(Cell $cell, $value) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php index 3d1dd22262d..7995c3b3fe9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php @@ -178,7 +178,7 @@ class Axis extends Properties * @param string $major_unit * @param string $minor_unit */ - public function setAxisOptionsProperties($axis_labels, $horizontal_crosses_value = null, $horizontal_crosses = null, $axis_orientation = null, $major_tmt = null, $minor_tmt = null, $minimum = null, $maximum = null, $major_unit = null, $minor_unit = null) + public function setAxisOptionsProperties($axis_labels, $horizontal_crosses_value = null, $horizontal_crosses = null, $axis_orientation = null, $major_tmt = null, $minor_tmt = null, $minimum = null, $maximum = null, $major_unit = null, $minor_unit = null): void { $this->axisOptions['axis_labels'] = (string) $axis_labels; ($horizontal_crosses_value !== null) ? $this->axisOptions['horizontal_crosses_value'] = (string) $horizontal_crosses_value : null; @@ -210,7 +210,7 @@ class Axis extends Properties * * @param string $orientation */ - public function setAxisOrientation($orientation) + public function setAxisOrientation($orientation): void { $this->axisOptions['orientation'] = (string) $orientation; } @@ -222,7 +222,7 @@ class Axis extends Properties * @param int $alpha * @param string $type */ - public function setFillParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB) + public function setFillParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB): void { $this->fillProperties = $this->setColorProperties($color, $alpha, $type); } @@ -234,7 +234,7 @@ class Axis extends Properties * @param int $alpha * @param string $type */ - public function setLineParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB) + public function setLineParameters($color, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_ARGB): void { $this->lineProperties = $this->setColorProperties($color, $alpha, $type); } @@ -276,7 +276,7 @@ class Axis extends Properties * @param string $end_arrow_type * @param string $end_arrow_size */ - public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null) + public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null): void { ($line_width !== null) ? $this->lineStyleProperties['width'] = $this->getExcelPointsWidth((float) $line_width) : null; ($compound_type !== null) ? $this->lineStyleProperties['compound'] = (string) $compound_type : null; @@ -336,7 +336,7 @@ class Axis extends Properties * @param int $sh_angle * @param float $sh_distance */ - public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null) + public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null): void { $this->setShadowPresetsProperties((int) $sh_presets) ->setShadowColor( @@ -354,7 +354,7 @@ class Axis extends Properties * * @param int $shadow_presets * - * @return Axis + * @return $this */ private function setShadowPresetsProperties($shadow_presets) { @@ -367,10 +367,9 @@ class Axis extends Properties /** * Set Shadow Properties from Mapped Values. * - * @param array $properties_map * @param mixed &$reference * - * @return Axis + * @return $this */ private function setShadowProperiesMapValues(array $properties_map, &$reference = null) { @@ -402,7 +401,7 @@ class Axis extends Properties * @param int $alpha * @param string $type * - * @return Axis + * @return $this */ private function setShadowColor($color, $alpha, $type) { @@ -416,7 +415,7 @@ class Axis extends Properties * * @param float $blur * - * @return Axis + * @return $this */ private function setShadowBlur($blur) { @@ -432,7 +431,7 @@ class Axis extends Properties * * @param int $angle * - * @return Axis + * @return $this */ private function setShadowAngle($angle) { @@ -448,7 +447,7 @@ class Axis extends Properties * * @param float $distance * - * @return Axis + * @return $this */ private function setShadowDistance($distance) { @@ -479,7 +478,7 @@ class Axis extends Properties * @param int $color_alpha * @param string $color_type */ - public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null) + public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null): void { $this->setGlowSize($size) ->setGlowColor( @@ -506,7 +505,7 @@ class Axis extends Properties * * @param float $size * - * @return Axis + * @return $this */ private function setGlowSize($size) { @@ -524,7 +523,7 @@ class Axis extends Properties * @param int $alpha * @param string $type * - * @return Axis + * @return $this */ private function setGlowColor($color, $alpha, $type) { @@ -538,7 +537,7 @@ class Axis extends Properties * * @param float $size */ - public function setSoftEdges($size) + public function setSoftEdges($size): void { if ($size !== null) { $softEdges['size'] = (string) $this->getExcelPointsWidth($size); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php index 921795da941..20eb2aee7b6 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php @@ -68,7 +68,7 @@ class Chart * * @var string */ - private $displayBlanksAs = '0'; + private $displayBlanksAs = DataSeries::EMPTY_AS_GAP; /** * Chart Asix Y as. @@ -144,19 +144,10 @@ class Chart * Create a new Chart. * * @param mixed $name - * @param null|Title $title - * @param null|Legend $legend - * @param null|PlotArea $plotArea * @param mixed $plotVisibleOnly - * @param mixed $displayBlanksAs - * @param null|Title $xAxisLabel - * @param null|Title $yAxisLabel - * @param null|Axis $xAxis - * @param null|Axis $yAxis - * @param null|GridLines $majorGridlines - * @param null|GridLines $minorGridlines + * @param string $displayBlanksAs */ - public function __construct($name, Title $title = null, Legend $legend = null, PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = 'gap', Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null) + public function __construct($name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null) { $this->name = $name; $this->title = $title; @@ -197,9 +188,9 @@ class Chart * * @param Worksheet $pValue * - * @return Chart + * @return $this */ - public function setWorksheet(Worksheet $pValue = null) + public function setWorksheet(?Worksheet $pValue = null) { $this->worksheet = $pValue; @@ -219,9 +210,7 @@ class Chart /** * Set Title. * - * @param Title $title - * - * @return Chart + * @return $this */ public function setTitle(Title $title) { @@ -243,9 +232,7 @@ class Chart /** * Set Legend. * - * @param Legend $legend - * - * @return Chart + * @return $this */ public function setLegend(Legend $legend) { @@ -267,9 +254,7 @@ class Chart /** * Set X-Axis Label. * - * @param Title $label - * - * @return Chart + * @return $this */ public function setXAxisLabel(Title $label) { @@ -291,9 +276,7 @@ class Chart /** * Set Y-Axis Label. * - * @param Title $label - * - * @return Chart + * @return $this */ public function setYAxisLabel(Title $label) { @@ -327,7 +310,7 @@ class Chart * * @param bool $plotVisibleOnly * - * @return Chart + * @return $this */ public function setPlotVisibleOnly($plotVisibleOnly) { @@ -351,7 +334,7 @@ class Chart * * @param string $displayBlanksAs * - * @return Chart + * @return $this */ public function setDisplayBlanksAs($displayBlanksAs) { @@ -423,7 +406,7 @@ class Chart * @param int $xOffset * @param int $yOffset * - * @return Chart + * @return $this */ public function setTopLeftPosition($cell, $xOffset = null, $yOffset = null) { @@ -467,7 +450,7 @@ class Chart * * @param string $cell * - * @return Chart + * @return $this */ public function setTopLeftCell($cell) { @@ -482,7 +465,7 @@ class Chart * @param int $xOffset * @param int $yOffset * - * @return Chart + * @return $this */ public function setTopLeftOffset($xOffset, $yOffset) { @@ -541,7 +524,7 @@ class Chart * @param int $xOffset * @param int $yOffset * - * @return Chart + * @return $this */ public function setBottomRightPosition($cell, $xOffset = null, $yOffset = null) { @@ -593,7 +576,7 @@ class Chart * @param int $xOffset * @param int $yOffset * - * @return Chart + * @return $this */ public function setBottomRightOffset($xOffset, $yOffset) { @@ -645,7 +628,7 @@ class Chart return $this->bottomRightYOffset; } - public function refresh() + public function refresh(): void { if ($this->worksheet !== null) { $this->plotArea->refresh($this->worksheet); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php index 8056bbeea27..3a44b33529b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php @@ -40,6 +40,10 @@ class DataSeries const STYLE_MARKER = 'marker'; const STYLE_FILLED = 'filled'; + const EMPTY_AS_GAP = 'gap'; + const EMPTY_AS_ZERO = 'zero'; + const EMPTY_AS_SPAN = 'span'; + /** * Series Plot Type. * @@ -157,7 +161,7 @@ class DataSeries * * @param string $plotType * - * @return DataSeries + * @return $this */ public function setPlotType($plotType) { @@ -181,7 +185,7 @@ class DataSeries * * @param string $groupingType * - * @return DataSeries + * @return $this */ public function setPlotGrouping($groupingType) { @@ -205,7 +209,7 @@ class DataSeries * * @param string $plotDirection * - * @return DataSeries + * @return $this */ public function setPlotDirection($plotDirection) { @@ -297,7 +301,7 @@ class DataSeries * * @param null|string $plotStyle * - * @return DataSeries + * @return $this */ public function setPlotStyle($plotStyle) { @@ -360,7 +364,7 @@ class DataSeries * * @param bool $smoothLine * - * @return DataSeries + * @return $this */ public function setSmoothLine($smoothLine) { @@ -369,7 +373,7 @@ class DataSeries return $this; } - public function refresh(Worksheet $worksheet) + public function refresh(Worksheet $worksheet): void { foreach ($this->plotValues as $plotValues) { if ($plotValues !== null) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php index a568e3fa92a..c1bd973a46d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -115,9 +115,7 @@ class DataSeriesValues * DataSeriesValues::DATASERIES_TYPE_NUMBER * Normally used for chart data values * - * @throws Exception - * - * @return DataSeriesValues + * @return $this */ public function setDataType($dataType) { @@ -144,7 +142,7 @@ class DataSeriesValues * * @param string $dataSource * - * @return DataSeriesValues + * @return $this */ public function setDataSource($dataSource) { @@ -168,7 +166,7 @@ class DataSeriesValues * * @param string $marker * - * @return DataSeriesValues + * @return $this */ public function setPointMarker($marker) { @@ -192,7 +190,7 @@ class DataSeriesValues * * @param string $formatCode * - * @return DataSeriesValues + * @return $this */ public function setFormatCode($formatCode) { @@ -247,8 +245,6 @@ class DataSeriesValues * * @param string $color value for color * - * @throws \Exception thrown if color is invalid - * * @return bool true if validation was successful */ private function validateColor($color) @@ -275,7 +271,7 @@ class DataSeriesValues * * @param int $width * - * @return DataSeriesValues + * @return $this */ public function setLineWidth($width) { @@ -346,7 +342,7 @@ class DataSeriesValues * * @param array $dataValues * - * @return DataSeriesValues + * @return $this */ public function setDataValues($dataValues) { @@ -356,7 +352,7 @@ class DataSeriesValues return $this; } - public function refresh(Worksheet $worksheet, $flatten = true) + public function refresh(Worksheet $worksheet, $flatten = true): void { if ($this->dataSource !== null) { $calcEngine = Calculation::getInstance($worksheet->getParent()); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php index 8cc83e5540c..2e424bc246a 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php @@ -91,7 +91,7 @@ class GridLines extends Properties /** * Change Object State to True. * - * @return GridLines + * @return $this */ private function activateObject() { @@ -107,7 +107,7 @@ class GridLines extends Properties * @param int $alpha * @param string $type */ - public function setLineColorProperties($value, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_STANDARD) + public function setLineColorProperties($value, $alpha = 0, $type = self::EXCEL_COLOR_TYPE_STANDARD): void { $this->activateObject() ->lineProperties['color'] = $this->setColorProperties( @@ -130,7 +130,7 @@ class GridLines extends Properties * @param string $end_arrow_type * @param string $end_arrow_size */ - public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null) + public function setLineStyleProperties($line_width = null, $compound_type = null, $dash_type = null, $cap_type = null, $join_type = null, $head_arrow_type = null, $head_arrow_size = null, $end_arrow_type = null, $end_arrow_size = null): void { $this->activateObject(); ($line_width !== null) @@ -194,7 +194,7 @@ class GridLines extends Properties * @param int $color_alpha * @param string $color_type */ - public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null) + public function setGlowProperties($size, $color_value = null, $color_alpha = null, $color_type = null): void { $this ->activateObject() @@ -229,7 +229,7 @@ class GridLines extends Properties * * @param float $size * - * @return GridLines + * @return $this */ private function setGlowSize($size) { @@ -245,7 +245,7 @@ class GridLines extends Properties * @param int $alpha * @param string $type * - * @return GridLines + * @return $this */ private function setGlowColor($color, $alpha, $type) { @@ -286,7 +286,7 @@ class GridLines extends Properties * @param int $sh_angle * @param float $sh_distance */ - public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null) + public function setShadowProperties($sh_presets, $sh_color_value = null, $sh_color_type = null, $sh_color_alpha = null, $sh_blur = null, $sh_angle = null, $sh_distance = null): void { $this->activateObject() ->setShadowPresetsProperties((int) $sh_presets) @@ -305,7 +305,7 @@ class GridLines extends Properties * * @param int $shadow_presets * - * @return GridLines + * @return $this */ private function setShadowPresetsProperties($shadow_presets) { @@ -318,10 +318,9 @@ class GridLines extends Properties /** * Set Shadow Properties Values. * - * @param array $properties_map * @param mixed &$reference * - * @return GridLines + * @return $this */ private function setShadowProperiesMapValues(array $properties_map, &$reference = null) { @@ -353,7 +352,7 @@ class GridLines extends Properties * @param int $alpha * @param string $type * - * @return GridLines + * @return $this */ private function setShadowColor($color, $alpha, $type) { @@ -375,7 +374,7 @@ class GridLines extends Properties * * @param float $blur * - * @return GridLines + * @return $this */ private function setShadowBlur($blur) { @@ -391,7 +390,7 @@ class GridLines extends Properties * * @param int $angle * - * @return GridLines + * @return $this */ private function setShadowAngle($angle) { @@ -407,7 +406,7 @@ class GridLines extends Properties * * @param float $distance * - * @return GridLines + * @return $this */ private function setShadowDistance($distance) { @@ -435,7 +434,7 @@ class GridLines extends Properties * * @param float $size */ - public function setSoftEdgesSize($size) + public function setSoftEdgesSize($size): void { if ($size !== null) { $this->activateObject(); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php index a8a96d2f1cb..51c8995b064 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php @@ -110,8 +110,6 @@ class Layout /** * Create a new Layout. - * - * @param array $layout */ public function __construct(array $layout = []) { @@ -153,7 +151,7 @@ class Layout * * @param string $value * - * @return Layout + * @return $this */ public function setLayoutTarget($value) { @@ -177,7 +175,7 @@ class Layout * * @param string $value * - * @return Layout + * @return $this */ public function setXMode($value) { @@ -201,7 +199,7 @@ class Layout * * @param string $value * - * @return Layout + * @return $this */ public function setYMode($value) { @@ -225,7 +223,7 @@ class Layout * * @param float $value * - * @return Layout + * @return $this */ public function setXPosition($value) { @@ -249,7 +247,7 @@ class Layout * * @param float $value * - * @return Layout + * @return $this */ public function setYPosition($value) { @@ -273,7 +271,7 @@ class Layout * * @param float $value * - * @return Layout + * @return $this */ public function setWidth($value) { @@ -297,7 +295,7 @@ class Layout * * @param float $value * - * @return Layout + * @return $this */ public function setHeight($value) { @@ -322,7 +320,7 @@ class Layout * * @param bool $value Show legend key * - * @return Layout + * @return $this */ public function setShowLegendKey($value) { @@ -347,7 +345,7 @@ class Layout * * @param bool $value Show val * - * @return Layout + * @return $this */ public function setShowVal($value) { @@ -372,7 +370,7 @@ class Layout * * @param bool $value Show cat name * - * @return Layout + * @return $this */ public function setShowCatName($value) { @@ -397,7 +395,7 @@ class Layout * * @param bool $value Show series name * - * @return Layout + * @return $this */ public function setShowSerName($value) { @@ -422,7 +420,7 @@ class Layout * * @param bool $value Show percentage * - * @return Layout + * @return $this */ public function setShowPercent($value) { @@ -447,7 +445,7 @@ class Layout * * @param bool $value Show bubble size * - * @return Layout + * @return $this */ public function setShowBubbleSize($value) { @@ -472,7 +470,7 @@ class Layout * * @param bool $value Show leader lines * - * @return Layout + * @return $this */ public function setShowLeaderLines($value) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php index d0776265105..fc0ed1407c0 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php @@ -52,10 +52,9 @@ class Legend * Create a new Legend. * * @param string $position - * @param null|Layout $layout * @param bool $overlay */ - public function __construct($position = self::POSITION_RIGHT, Layout $layout = null, $overlay = false) + public function __construct($position = self::POSITION_RIGHT, ?Layout $layout = null, $overlay = false) { $this->setPosition($position); $this->layout = $layout; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php index b98c638df76..954777cf2f5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php @@ -23,10 +23,9 @@ class PlotArea /** * Create a new PlotArea. * - * @param null|Layout $layout * @param DataSeries[] $plotSeries */ - public function __construct(Layout $layout = null, array $plotSeries = []) + public function __construct(?Layout $layout = null, array $plotSeries = []) { $this->layout = $layout; $this->plotSeries = $plotSeries; @@ -94,7 +93,7 @@ class PlotArea * * @param DataSeries[] $plotSeries * - * @return PlotArea + * @return $this */ public function setPlotSeries(array $plotSeries) { @@ -103,7 +102,7 @@ class PlotArea return $this; } - public function refresh(Worksheet $worksheet) + public function refresh(Worksheet $worksheet): void { foreach ($this->plotSeries as $plotSeries) { $plotSeries->refresh($worksheet); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php index c6fcfbfc938..3032f6ba25a 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php @@ -8,8 +8,6 @@ interface IRenderer { /** * IRenderer constructor. - * - * @param \PhpOffice\PhpSpreadsheet\Chart\Chart $chart */ public function __construct(Chart $chart); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php index 9dcab049d2e..02fbfed7ea3 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php @@ -2,10 +2,24 @@ namespace PhpOffice\PhpSpreadsheet\Chart\Renderer; +use AccBarPlot; +use AccLinePlot; +use BarPlot; +use ContourPlot; +use Graph; +use GroupBarPlot; +use LinePlot; use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; - -require_once __DIR__ . '/Polyfill.php'; +use PieGraph; +use PiePlot; +use PiePlot3D; +use PiePlotC; +use RadarGraph; +use RadarPlot; +use ScatterPlot; +use Spline; +use StockPlot; class JpGraph implements IRenderer { @@ -33,8 +47,6 @@ class JpGraph implements IRenderer /** * Create a new jpgraph. - * - * @param Chart $chart */ public function __construct(Chart $chart) { @@ -43,7 +55,7 @@ class JpGraph implements IRenderer $this->chart = $chart; } - private static function init() + private static function init(): void { static $loaded = false; if ($loaded) { @@ -179,7 +191,7 @@ class JpGraph implements IRenderer return $caption; } - private function renderTitle() + private function renderTitle(): void { $title = $this->getCaption($this->chart->getTitle()); if ($title !== null) { @@ -187,7 +199,7 @@ class JpGraph implements IRenderer } } - private function renderLegend() + private function renderLegend(): void { $legend = $this->chart->getLegend(); if ($legend !== null) { @@ -205,9 +217,11 @@ class JpGraph implements IRenderer break; case 't': $this->graph->legend->SetPos(0.5, 0.01, 'center', 'top'); // top + break; case 'b': $this->graph->legend->SetPos(0.5, 0.99, 'center', 'bottom'); // bottom + break; default: $this->graph->legend->SetPos(0.01, 0.01, 'right', 'top'); // top-right @@ -220,9 +234,9 @@ class JpGraph implements IRenderer } } - private function renderCartesianPlotArea($type = 'textlin') + private function renderCartesianPlotArea($type = 'textlin'): void { - $this->graph = new \Graph(self::$width, self::$height); + $this->graph = new Graph(self::$width, self::$height); $this->graph->SetScale($type); $this->renderTitle(); @@ -257,22 +271,22 @@ class JpGraph implements IRenderer } } - private function renderPiePlotArea() + private function renderPiePlotArea(): void { - $this->graph = new \PieGraph(self::$width, self::$height); + $this->graph = new PieGraph(self::$width, self::$height); $this->renderTitle(); } - private function renderRadarPlotArea() + private function renderRadarPlotArea(): void { - $this->graph = new \RadarGraph(self::$width, self::$height); + $this->graph = new RadarGraph(self::$width, self::$height); $this->graph->SetScale('lin'); $this->renderTitle(); } - private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d') + private function renderPlotLine($groupID, $filled = false, $combination = false, $dimensions = '2d'): void { $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); @@ -308,7 +322,7 @@ class JpGraph implements IRenderer ++$testCurrentIndex; } - $seriesPlot = new \LinePlot($dataValues); + $seriesPlot = new LinePlot($dataValues); if ($combination) { $seriesPlot->SetBarCenter(); } @@ -330,12 +344,12 @@ class JpGraph implements IRenderer if ($grouping == 'standard') { $groupPlot = $seriesPlots; } else { - $groupPlot = new \AccLinePlot($seriesPlots); + $groupPlot = new AccLinePlot($seriesPlots); } $this->graph->Add($groupPlot); } - private function renderPlotBar($groupID, $dimensions = '2d') + private function renderPlotBar($groupID, $dimensions = '2d'): void { $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection(); // Rotate for bar rather than column chart @@ -385,7 +399,7 @@ class JpGraph implements IRenderer if ($rotation == 'bar') { $dataValues = array_reverse($dataValues); } - $seriesPlot = new \BarPlot($dataValues); + $seriesPlot = new BarPlot($dataValues); $seriesPlot->SetColor('black'); $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]); if ($dimensions == '3d') { @@ -406,11 +420,11 @@ class JpGraph implements IRenderer } if ($grouping == 'clustered') { - $groupPlot = new \GroupBarPlot($seriesPlots); + $groupPlot = new GroupBarPlot($seriesPlots); } elseif ($grouping == 'standard') { - $groupPlot = new \GroupBarPlot($seriesPlots); + $groupPlot = new GroupBarPlot($seriesPlots); } else { - $groupPlot = new \AccBarPlot($seriesPlots); + $groupPlot = new AccBarPlot($seriesPlots); if ($dimensions == '3d') { $groupPlot->SetShadow(); } @@ -419,7 +433,7 @@ class JpGraph implements IRenderer $this->graph->Add($groupPlot); } - private function renderPlotScatter($groupID, $bubble) + private function renderPlotScatter($groupID, $bubble): void { $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping(); $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); @@ -436,14 +450,14 @@ class JpGraph implements IRenderer $dataValuesY[$k] = $k; } - $seriesPlot = new \ScatterPlot($dataValuesX, $dataValuesY); + $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY); if ($scatterStyle == 'lineMarker') { $seriesPlot->SetLinkPoints(); $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]); } elseif ($scatterStyle == 'smoothMarker') { - $spline = new \Spline($dataValuesY, $dataValuesX); + $spline = new Spline($dataValuesY, $dataValuesX); [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20); - $lplot = new \LinePlot($splineDataX, $splineDataY); + $lplot = new LinePlot($splineDataX, $splineDataY); $lplot->SetColor(self::$colourSet[self::$plotColour]); $this->graph->Add($lplot); @@ -464,7 +478,7 @@ class JpGraph implements IRenderer } } - private function renderPlotRadar($groupID) + private function renderPlotRadar($groupID): void { $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); @@ -488,7 +502,7 @@ class JpGraph implements IRenderer $this->graph->SetTitles(array_reverse($dataValues)); - $seriesPlot = new \RadarPlot(array_reverse($dataValuesX)); + $seriesPlot = new RadarPlot(array_reverse($dataValuesX)); $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue(); $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]); @@ -502,7 +516,7 @@ class JpGraph implements IRenderer } } - private function renderPlotContour($groupID) + private function renderPlotContour($groupID): void { $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle(); @@ -517,12 +531,12 @@ class JpGraph implements IRenderer $dataValues[$i] = $dataValuesX; } - $seriesPlot = new \ContourPlot($dataValues); + $seriesPlot = new ContourPlot($dataValues); $this->graph->Add($seriesPlot); } - private function renderPlotStock($groupID) + private function renderPlotStock($groupID): void { $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount(); $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder(); @@ -556,13 +570,13 @@ class JpGraph implements IRenderer $this->graph->xaxis->SetTickLabels($datasetLabels); } - $seriesPlot = new \StockPlot($dataValuesPlot); + $seriesPlot = new StockPlot($dataValuesPlot); $seriesPlot->SetWidth(20); $this->graph->Add($seriesPlot); } - private function renderAreaChart($groupCount, $dimensions = '2d') + private function renderAreaChart($groupCount, $dimensions = '2d'): void { $this->renderCartesianPlotArea(); @@ -571,7 +585,7 @@ class JpGraph implements IRenderer } } - private function renderLineChart($groupCount, $dimensions = '2d') + private function renderLineChart($groupCount, $dimensions = '2d'): void { $this->renderCartesianPlotArea(); @@ -580,7 +594,7 @@ class JpGraph implements IRenderer } } - private function renderBarChart($groupCount, $dimensions = '2d') + private function renderBarChart($groupCount, $dimensions = '2d'): void { $this->renderCartesianPlotArea(); @@ -589,7 +603,7 @@ class JpGraph implements IRenderer } } - private function renderScatterChart($groupCount) + private function renderScatterChart($groupCount): void { $this->renderCartesianPlotArea('linlin'); @@ -598,7 +612,7 @@ class JpGraph implements IRenderer } } - private function renderBubbleChart($groupCount) + private function renderBubbleChart($groupCount): void { $this->renderCartesianPlotArea('linlin'); @@ -607,7 +621,7 @@ class JpGraph implements IRenderer } } - private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false) + private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false): void { $this->renderPiePlotArea(); @@ -643,12 +657,12 @@ class JpGraph implements IRenderer } if ($dimensions == '3d') { - $seriesPlot = new \PiePlot3D($dataValues); + $seriesPlot = new PiePlot3D($dataValues); } else { if ($doughnut) { - $seriesPlot = new \PiePlotC($dataValues); + $seriesPlot = new PiePlotC($dataValues); } else { - $seriesPlot = new \PiePlot($dataValues); + $seriesPlot = new PiePlot($dataValues); } } @@ -679,7 +693,7 @@ class JpGraph implements IRenderer } } - private function renderRadarChart($groupCount) + private function renderRadarChart($groupCount): void { $this->renderRadarPlotArea(); @@ -688,7 +702,7 @@ class JpGraph implements IRenderer } } - private function renderStockChart($groupCount) + private function renderStockChart($groupCount): void { $this->renderCartesianPlotArea('intint'); @@ -697,7 +711,7 @@ class JpGraph implements IRenderer } } - private function renderContourChart($groupCount, $dimensions) + private function renderContourChart($groupCount, $dimensions): void { $this->renderCartesianPlotArea('intint'); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php deleted file mode 100644 index 7fa383944c9..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/Polyfill.php +++ /dev/null @@ -1,9 +0,0 @@ -caption = $caption; $this->layout = $layout; @@ -45,7 +44,7 @@ class Title * * @param string $caption * - * @return Title + * @return $this */ public function setCaption($caption) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php index 84c3d3009ca..48f34f41d24 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Collection; +use Generator; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; @@ -61,7 +62,6 @@ class Cells * Initialise this new cell collection. * * @param Worksheet $parent The worksheet for this cell collection - * @param CacheInterface $cache */ public function __construct(Worksheet $parent, CacheInterface $cache) { @@ -105,8 +105,6 @@ class Cells * * @param Cell $cell Cell to update * - * @throws PhpSpreadsheetException - * * @return Cell */ public function update(Cell $cell) @@ -119,7 +117,7 @@ class Cells * * @param string $pCoord Coordinate of the cell to delete */ - public function delete($pCoord) + public function delete($pCoord): void { if ($pCoord === $this->currentCoordinate && $this->currentCell !== null) { $this->currentCell->detach(); @@ -348,7 +346,7 @@ class Cells * * @param string $row Row number to remove */ - public function removeRow($row) + public function removeRow($row): void { foreach ($this->getCoordinates() as $coord) { $c = ''; @@ -366,7 +364,7 @@ class Cells * * @param string $column Column ID to remove */ - public function removeColumn($column) + public function removeColumn($column): void { foreach ($this->getCoordinates() as $coord) { $c = ''; @@ -382,10 +380,8 @@ class Cells /** * Store cell data in cache for the current cell object if it's "dirty", * and the 'nullify' the current cell object. - * - * @throws PhpSpreadsheetException */ - private function storeCurrentCell() + private function storeCurrentCell(): void { if ($this->currentCellIsDirty && !empty($this->currentCoordinate)) { $this->currentCell->detach(); @@ -409,8 +405,6 @@ class Cells * @param string $pCoord Coordinate of the cell to update * @param Cell $cell Cell to update * - * @throws PhpSpreadsheetException - * * @return \PhpOffice\PhpSpreadsheet\Cell\Cell */ public function add($pCoord, Cell $cell) @@ -432,9 +426,7 @@ class Cells * * @param string $pCoord Coordinate of the cell * - * @throws PhpSpreadsheetException - * - * @return \PhpOffice\PhpSpreadsheet\Cell\Cell Cell that was found, or null if not found + * @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell Cell that was found, or null if not found */ public function get($pCoord) { @@ -467,7 +459,7 @@ class Cells /** * Clear the cell collection and disconnect from our parent. */ - public function unsetWorksheetCells() + public function unsetWorksheetCells(): void { if ($this->currentCell !== null) { $this->currentCell->detach(); @@ -495,7 +487,7 @@ class Cells /** * Returns all known cache keys. * - * @return \Generator|string[] + * @return Generator|string[] */ private function getAllCacheKeys() { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php index 1b5ab1fd2c1..31f7664053f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php @@ -96,7 +96,7 @@ class Comment implements IComparable * * @param string $author * - * @return Comment + * @return $this */ public function setAuthor($author) { @@ -118,9 +118,7 @@ class Comment implements IComparable /** * Set Rich text comment. * - * @param RichText $pValue - * - * @return Comment + * @return $this */ public function setText(RichText $pValue) { @@ -144,7 +142,7 @@ class Comment implements IComparable * * @param string $width * - * @return Comment + * @return $this */ public function setWidth($width) { @@ -168,7 +166,7 @@ class Comment implements IComparable * * @param string $value * - * @return Comment + * @return $this */ public function setHeight($value) { @@ -192,7 +190,7 @@ class Comment implements IComparable * * @param string $value * - * @return Comment + * @return $this */ public function setMarginLeft($value) { @@ -216,7 +214,7 @@ class Comment implements IComparable * * @param string $value * - * @return Comment + * @return $this */ public function setMarginTop($value) { @@ -240,7 +238,7 @@ class Comment implements IComparable * * @param bool $value * - * @return Comment + * @return $this */ public function setVisible($value) { @@ -264,7 +262,7 @@ class Comment implements IComparable * * @param string $alignment see Style\Alignment::HORIZONTAL_* * - * @return Comment + * @return $this */ public function setAlignment($alignment) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php new file mode 100644 index 00000000000..dbadd4ced57 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php @@ -0,0 +1,263 @@ +worksheet). + * + * @var bool + */ + protected $localOnly; + + /** + * Scope. + * + * @var Worksheet + */ + protected $scope; + + /** + * Whether this is a named range or a named formula. + * + * @var bool + */ + protected $isFormula; + + /** + * Create a new Defined Name. + */ + public function __construct( + string $name, + ?Worksheet $worksheet = null, + ?string $value = null, + bool $localOnly = false, + ?Worksheet $scope = null + ) { + if ($worksheet === null) { + $worksheet = $scope; + } + + // Set local members + $this->name = $name; + $this->worksheet = $worksheet; + $this->value = (string) $value; + $this->localOnly = $localOnly; + // If local only, then the scope will be set to worksheet unless a scope is explicitly set + $this->scope = ($localOnly === true) ? (($scope === null) ? $worksheet : $scope) : null; + // If the range string contains characters that aren't associated with the range definition (A-Z,1-9 + // for cell references, and $, or the range operators (colon comma or space), quotes and ! for + // worksheet names + // then this is treated as a named formula, and not a named range + $this->isFormula = self::testIfFormula($this->value); + } + + /** + * Create a new defined name, either a range or a formula. + */ + public static function createInstance( + string $name, + ?Worksheet $worksheet = null, + ?string $value = null, + bool $localOnly = false, + ?Worksheet $scope = null + ): self { + $value = (string) $value; + $isFormula = self::testIfFormula($value); + if ($isFormula) { + return new NamedFormula($name, $worksheet, $value, $localOnly, $scope); + } + + return new NamedRange($name, $worksheet, $value, $localOnly, $scope); + } + + public static function testIfFormula(string $value): bool + { + if (substr($value, 0, 1) === '=') { + $value = substr($value, 1); + } + + if (is_numeric($value)) { + return true; + } + + $segMatcher = false; + foreach (explode("'", $value) as $subVal) { + // Only test in alternate array entries (the non-quoted blocks) + if ( + ($segMatcher = !$segMatcher) && + (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal)) + ) { + return true; + } + } + + return false; + } + + /** + * Get name. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Set name. + */ + public function setName(string $name): self + { + if (!empty($name)) { + // Old title + $oldTitle = $this->name; + + // Re-attach + if ($this->worksheet !== null) { + $this->worksheet->getParent()->removeNamedRange($this->name, $this->worksheet); + } + $this->name = $name; + + if ($this->worksheet !== null) { + $this->worksheet->getParent()->addNamedRange($this); + } + + // New title + $newTitle = $this->name; + ReferenceHelper::getInstance()->updateNamedFormulas($this->worksheet->getParent(), $oldTitle, $newTitle); + } + + return $this; + } + + /** + * Get worksheet. + */ + public function getWorksheet(): ?Worksheet + { + return $this->worksheet; + } + + /** + * Set worksheet. + */ + public function setWorksheet(?Worksheet $value): self + { + $this->worksheet = $value; + + return $this; + } + + /** + * Get range or formula value. + */ + public function getValue(): string + { + return $this->value; + } + + /** + * Set range or formula value. + */ + public function setValue(string $value): self + { + $this->value = $value; + + return $this; + } + + /** + * Get localOnly. + */ + public function getLocalOnly(): bool + { + return $this->localOnly; + } + + /** + * Set localOnly. + */ + public function setLocalOnly(bool $value): self + { + $this->localOnly = $value; + $this->scope = $value ? $this->worksheet : null; + + return $this; + } + + /** + * Get scope. + */ + public function getScope(): ?Worksheet + { + return $this->scope; + } + + /** + * Set scope. + */ + public function setScope(?Worksheet $value): self + { + $this->scope = $value; + $this->localOnly = $value !== null; + + return $this; + } + + /** + * Identify whether this is a named range or a named formula. + */ + public function isFormula(): bool + { + return $this->isFormula; + } + + /** + * Resolve a named range to a regular cell range or formula. + */ + public static function resolveName(string $pDefinedName, Worksheet $pSheet): ?self + { + return $pSheet->getParent()->getDefinedName($pDefinedName, $pSheet); + } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value)) { + $this->$key = clone $value; + } else { + $this->$key = $value; + } + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php index 1a432db0497..0876a9ed486 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php @@ -122,7 +122,7 @@ class Properties * * @param string $creator * - * @return Properties + * @return $this */ public function setCreator($creator) { @@ -146,7 +146,7 @@ class Properties * * @param string $pValue * - * @return Properties + * @return $this */ public function setLastModifiedBy($pValue) { @@ -170,7 +170,7 @@ class Properties * * @param int|string $time * - * @return Properties + * @return $this */ public function setCreated($time) { @@ -204,7 +204,7 @@ class Properties * * @param int|string $time * - * @return Properties + * @return $this */ public function setModified($time) { @@ -238,7 +238,7 @@ class Properties * * @param string $title * - * @return Properties + * @return $this */ public function setTitle($title) { @@ -262,7 +262,7 @@ class Properties * * @param string $description * - * @return Properties + * @return $this */ public function setDescription($description) { @@ -286,7 +286,7 @@ class Properties * * @param string $subject * - * @return Properties + * @return $this */ public function setSubject($subject) { @@ -310,7 +310,7 @@ class Properties * * @param string $keywords * - * @return Properties + * @return $this */ public function setKeywords($keywords) { @@ -334,7 +334,7 @@ class Properties * * @param string $category * - * @return Properties + * @return $this */ public function setCategory($category) { @@ -358,7 +358,7 @@ class Properties * * @param string $company * - * @return Properties + * @return $this */ public function setCompany($company) { @@ -382,7 +382,7 @@ class Properties * * @param string $manager * - * @return Properties + * @return $this */ public function setManager($manager) { @@ -453,15 +453,18 @@ class Properties * 'd' : Date/Time * 'b' : Boolean * - * @return Properties + * @return $this */ public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null) { - if (($propertyType === null) || (!in_array($propertyType, [self::PROPERTY_TYPE_INTEGER, - self::PROPERTY_TYPE_FLOAT, - self::PROPERTY_TYPE_STRING, - self::PROPERTY_TYPE_DATE, - self::PROPERTY_TYPE_BOOLEAN, ]))) { + if ( + ($propertyType === null) || (!in_array($propertyType, [self::PROPERTY_TYPE_INTEGER, + self::PROPERTY_TYPE_FLOAT, + self::PROPERTY_TYPE_STRING, + self::PROPERTY_TYPE_DATE, + self::PROPERTY_TYPE_BOOLEAN, + ])) + ) { if ($propertyValue === null) { $propertyType = self::PROPERTY_TYPE_STRING; } elseif (is_float($propertyValue)) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php index 1682678c2e7..cef3db8c44c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php @@ -75,7 +75,7 @@ class Security * * @param bool $pValue * - * @return Security + * @return $this */ public function setLockRevision($pValue) { @@ -99,7 +99,7 @@ class Security * * @param bool $pValue * - * @return Security + * @return $this */ public function setLockStructure($pValue) { @@ -123,7 +123,7 @@ class Security * * @param bool $pValue * - * @return Security + * @return $this */ public function setLockWindows($pValue) { @@ -148,7 +148,7 @@ class Security * @param string $pValue * @param bool $pAlreadyHashed If the password has already been hashed, set this to true * - * @return Security + * @return $this */ public function setRevisionsPassword($pValue, $pAlreadyHashed = false) { @@ -176,7 +176,7 @@ class Security * @param string $pValue * @param bool $pAlreadyHashed If the password has already been hashed, set this to true * - * @return Security + * @return $this */ public function setWorkbookPassword($pValue, $pAlreadyHashed = false) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php new file mode 100644 index 00000000000..5e06af97f41 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php @@ -0,0 +1,97 @@ + $category) { + $result .= "\n"; + $result .= "## {$categoryConstant}\n"; + $result .= "\n"; + $lengths = [20, 42]; + $result .= self::tableRow($lengths, ['Excel Function', 'PhpSpreadsheet Function']) . "\n"; + $result .= self::tableRow($lengths, null) . "\n"; + foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) { + if ($category === $functionInfo['category']) { + $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']); + $result .= self::tableRow($lengths, [$excelFunction, $phpFunction]) . "\n"; + } + } + } + + return $result; + } + + private static function getCategories(): array + { + return (new ReflectionClass(Category::class))->getConstants(); + } + + private static function tableRow(array $lengths, ?array $values = null): string + { + $result = ''; + foreach (array_map(null, $lengths, $values ?? []) as $i => [$length, $value]) { + $pad = $value === null ? '-' : ' '; + if ($i > 0) { + $result .= '|' . $pad; + } + $result .= str_pad($value ?? '', $length, $pad); + } + + return rtrim($result, ' '); + } + + private static function getPhpSpreadsheetFunctionText($functionCall): string + { + if (is_string($functionCall)) { + return $functionCall; + } + if ($functionCall === [Functions::class, 'DUMMY']) { + return '**Not yet Implemented**'; + } + if (is_array($functionCall)) { + return "\\{$functionCall[0]}::{$functionCall[1]}"; + } + + throw new UnexpectedValueException( + '$functionCall is of type ' . gettype($functionCall) . '. string or array expected' + ); + } + + /** + * @param array[] $phpSpreadsheetFunctions + */ + public static function generateFunctionListByName(array $phpSpreadsheetFunctions): string + { + $categoryConstants = array_flip(self::getCategories()); + $result = "# Function list by name\n"; + $lastAlphabet = null; + foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) { + $lengths = [20, 31, 42]; + if ($lastAlphabet !== $excelFunction[0]) { + $lastAlphabet = $excelFunction[0]; + $result .= "\n"; + $result .= "## {$lastAlphabet}\n"; + $result .= "\n"; + $result .= self::tableRow($lengths, ['Excel Function', 'Category', 'PhpSpreadsheet Function']) . "\n"; + $result .= self::tableRow($lengths, null) . "\n"; + } + $category = $categoryConstants[$functionInfo['category']]; + $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']); + $result .= self::tableRow($lengths, [$excelFunction, $category, $phpFunction]) . "\n"; + } + + return $result; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php index 4e8f0a5c912..90ea806bd8f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php @@ -22,8 +22,6 @@ class HashTable * Create a new \PhpOffice\PhpSpreadsheet\HashTable. * * @param IComparable[] $pSource Optional source array to create HashTable from - * - * @throws Exception */ public function __construct($pSource = null) { @@ -37,10 +35,8 @@ class HashTable * Add HashTable items from source. * * @param IComparable[] $pSource Source array to create HashTable from - * - * @throws Exception */ - public function addFromSource(array $pSource = null) + public function addFromSource(?array $pSource = null): void { // Check if an array was passed if ($pSource == null) { @@ -57,7 +53,7 @@ class HashTable * * @param IComparable $pSource Item to add */ - public function add(IComparable $pSource) + public function add(IComparable $pSource): void { $hash = $pSource->getHashCode(); if (!isset($this->items[$hash])) { @@ -71,7 +67,7 @@ class HashTable * * @param IComparable $pSource Item to remove */ - public function remove(IComparable $pSource) + public function remove(IComparable $pSource): void { $hash = $pSource->getHashCode(); if (isset($this->items[$hash])) { @@ -94,7 +90,7 @@ class HashTable /** * Clear HashTable. */ - public function clear() + public function clear(): void { $this->items = []; $this->keyMap = []; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php index eaf73028fa7..6c4cbf9bbb5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php @@ -593,7 +593,7 @@ class Html */ protected $richTextObject; - protected function initialise() + protected function initialise(): void { $this->face = $this->size = $this->color = null; $this->bold = $this->italic = $this->underline = $this->superscript = $this->subscript = $this->strikethrough = false; @@ -632,7 +632,7 @@ class Html return $this->richTextObject; } - protected function cleanWhitespace() + protected function cleanWhitespace(): void { foreach ($this->richTextObject->getRichTextElements() as $key => $element) { $text = $element->getText(); @@ -646,7 +646,7 @@ class Html } } - protected function buildTextRun() + protected function buildTextRun(): void { $text = $this->stringData; if (trim($text) === '') { @@ -691,15 +691,15 @@ class Html $value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); } - return implode($values[0]); + return implode('', $values[0]); } - protected function colourNameLookup($rgb) + public static function colourNameLookup(string $rgb): string { - return self::$colourMap[$rgb]; + return self::$colourMap[$rgb] ?? ''; } - protected function startFontTag($tag) + protected function startFontTag($tag): void { foreach ($tag->attributes as $attribute) { $attributeName = strtolower($attribute->name); @@ -719,77 +719,77 @@ class Html } } - protected function endFontTag() + protected function endFontTag(): void { $this->face = $this->size = $this->color = null; } - protected function startBoldTag() + protected function startBoldTag(): void { $this->bold = true; } - protected function endBoldTag() + protected function endBoldTag(): void { $this->bold = false; } - protected function startItalicTag() + protected function startItalicTag(): void { $this->italic = true; } - protected function endItalicTag() + protected function endItalicTag(): void { $this->italic = false; } - protected function startUnderlineTag() + protected function startUnderlineTag(): void { $this->underline = true; } - protected function endUnderlineTag() + protected function endUnderlineTag(): void { $this->underline = false; } - protected function startSubscriptTag() + protected function startSubscriptTag(): void { $this->subscript = true; } - protected function endSubscriptTag() + protected function endSubscriptTag(): void { $this->subscript = false; } - protected function startSuperscriptTag() + protected function startSuperscriptTag(): void { $this->superscript = true; } - protected function endSuperscriptTag() + protected function endSuperscriptTag(): void { $this->superscript = false; } - protected function startStrikethruTag() + protected function startStrikethruTag(): void { $this->strikethrough = true; } - protected function endStrikethruTag() + protected function endStrikethruTag(): void { $this->strikethrough = false; } - protected function breakTag() + protected function breakTag(): void { $this->stringData .= "\n"; } - protected function parseTextNode(DOMText $textNode) + protected function parseTextNode(DOMText $textNode): void { $domText = preg_replace( '/\s+/u', @@ -801,11 +801,9 @@ class Html } /** - * @param DOMElement $element * @param string $callbackTag - * @param array $callbacks */ - protected function handleCallback(DOMElement $element, $callbackTag, array $callbacks) + protected function handleCallback(DOMElement $element, $callbackTag, array $callbacks): void { if (isset($callbacks[$callbackTag])) { $elementHandler = $callbacks[$callbackTag]; @@ -815,7 +813,7 @@ class Html } } - protected function parseElementNode(DOMElement $element) + protected function parseElementNode(DOMElement $element): void { $callbackTag = strtolower($element->nodeName); $this->stack[] = $callbackTag; @@ -828,7 +826,7 @@ class Html $this->handleCallback($element, $callbackTag, $this->endTagCallbacks); } - protected function parseElements(DOMNode $element) + protected function parseElements(DOMNode $element): void { foreach ($element->childNodes as $child) { if ($child instanceof DOMText) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Migrator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Migrator.php deleted file mode 100644 index 26d5fcead87..00000000000 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Migrator.php +++ /dev/null @@ -1,333 +0,0 @@ -from = array_keys($this->getMapping()); - $this->to = array_values($this->getMapping()); - } - - /** - * Return the ordered mapping from old PHPExcel class names to new PhpSpreadsheet one. - * - * @return string[] - */ - public function getMapping() - { - // Order matters here, we should have the deepest namespaces first (the most "unique" strings) - $classes = [ - 'PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip::class, - 'PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer::class, - 'PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE::class, - 'PHPExcel_Shared_Escher_DgContainer_SpgrContainer' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer::class, - 'PHPExcel_Shared_Escher_DggContainer_BstoreContainer' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer::class, - 'PHPExcel_Shared_OLE_PPS_File' => \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::class, - 'PHPExcel_Shared_OLE_PPS_Root' => \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root::class, - 'PHPExcel_Worksheet_AutoFilter_Column_Rule' => \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::class, - 'PHPExcel_Writer_OpenDocument_Cell_Comment' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment::class, - 'PHPExcel_Calculation_Token_Stack' => \PhpOffice\PhpSpreadsheet\Calculation\Token\Stack::class, - 'PHPExcel_Chart_Renderer_jpgraph' => \PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class, - 'PHPExcel_Reader_Excel5_Escher' => \PhpOffice\PhpSpreadsheet\Reader\Xls\Escher::class, - 'PHPExcel_Reader_Excel5_MD5' => \PhpOffice\PhpSpreadsheet\Reader\Xls\MD5::class, - 'PHPExcel_Reader_Excel5_RC4' => \PhpOffice\PhpSpreadsheet\Reader\Xls\RC4::class, - 'PHPExcel_Reader_Excel2007_Chart' => \PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart::class, - 'PHPExcel_Reader_Excel2007_Theme' => \PhpOffice\PhpSpreadsheet\Reader\Xlsx\Theme::class, - 'PHPExcel_Shared_Escher_DgContainer' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer::class, - 'PHPExcel_Shared_Escher_DggContainer' => \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer::class, - 'CholeskyDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\CholeskyDecomposition::class, - 'EigenvalueDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\EigenvalueDecomposition::class, - 'PHPExcel_Shared_JAMA_LUDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\LUDecomposition::class, - 'PHPExcel_Shared_JAMA_Matrix' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix::class, - 'QRDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\QRDecomposition::class, - 'PHPExcel_Shared_JAMA_QRDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\QRDecomposition::class, - 'SingularValueDecomposition' => \PhpOffice\PhpSpreadsheet\Shared\JAMA\SingularValueDecomposition::class, - 'PHPExcel_Shared_OLE_ChainedBlockStream' => \PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream::class, - 'PHPExcel_Shared_OLE_PPS' => \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS::class, - 'PHPExcel_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\BestFit::class, - 'PHPExcel_Exponential_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\ExponentialBestFit::class, - 'PHPExcel_Linear_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\LinearBestFit::class, - 'PHPExcel_Logarithmic_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\LogarithmicBestFit::class, - 'polynomialBestFit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\PolynomialBestFit::class, - 'PHPExcel_Polynomial_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\PolynomialBestFit::class, - 'powerBestFit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\PowerBestFit::class, - 'PHPExcel_Power_Best_Fit' => \PhpOffice\PhpSpreadsheet\Shared\Trend\PowerBestFit::class, - 'trendClass' => \PhpOffice\PhpSpreadsheet\Shared\Trend\Trend::class, - 'PHPExcel_Worksheet_AutoFilter_Column' => \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::class, - 'PHPExcel_Worksheet_Drawing_Shadow' => \PhpOffice\PhpSpreadsheet\Worksheet\Drawing\Shadow::class, - 'PHPExcel_Writer_OpenDocument_Content' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Content::class, - 'PHPExcel_Writer_OpenDocument_Meta' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Meta::class, - 'PHPExcel_Writer_OpenDocument_MetaInf' => \PhpOffice\PhpSpreadsheet\Writer\Ods\MetaInf::class, - 'PHPExcel_Writer_OpenDocument_Mimetype' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Mimetype::class, - 'PHPExcel_Writer_OpenDocument_Settings' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Settings::class, - 'PHPExcel_Writer_OpenDocument_Styles' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Styles::class, - 'PHPExcel_Writer_OpenDocument_Thumbnails' => \PhpOffice\PhpSpreadsheet\Writer\Ods\Thumbnails::class, - 'PHPExcel_Writer_OpenDocument_WriterPart' => \PhpOffice\PhpSpreadsheet\Writer\Ods\WriterPart::class, - 'PHPExcel_Writer_PDF_Core' => \PhpOffice\PhpSpreadsheet\Writer\Pdf::class, - 'PHPExcel_Writer_PDF_DomPDF' => \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class, - 'PHPExcel_Writer_PDF_mPDF' => \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf::class, - 'PHPExcel_Writer_PDF_tcPDF' => \PhpOffice\PhpSpreadsheet\Writer\Pdf\Tcpdf::class, - 'PHPExcel_Writer_Excel5_BIFFwriter' => \PhpOffice\PhpSpreadsheet\Writer\Xls\BIFFwriter::class, - 'PHPExcel_Writer_Excel5_Escher' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Escher::class, - 'PHPExcel_Writer_Excel5_Font' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Font::class, - 'PHPExcel_Writer_Excel5_Parser' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser::class, - 'PHPExcel_Writer_Excel5_Workbook' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::class, - 'PHPExcel_Writer_Excel5_Worksheet' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet::class, - 'PHPExcel_Writer_Excel5_Xf' => \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf::class, - 'PHPExcel_Writer_Excel2007_Chart' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Chart::class, - 'PHPExcel_Writer_Excel2007_Comments' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Comments::class, - 'PHPExcel_Writer_Excel2007_ContentTypes' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\ContentTypes::class, - 'PHPExcel_Writer_Excel2007_DocProps' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\DocProps::class, - 'PHPExcel_Writer_Excel2007_Drawing' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Drawing::class, - 'PHPExcel_Writer_Excel2007_Rels' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels::class, - 'PHPExcel_Writer_Excel2007_RelsRibbon' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon::class, - 'PHPExcel_Writer_Excel2007_RelsVBA' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA::class, - 'PHPExcel_Writer_Excel2007_StringTable' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable::class, - 'PHPExcel_Writer_Excel2007_Style' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style::class, - 'PHPExcel_Writer_Excel2007_Theme' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme::class, - 'PHPExcel_Writer_Excel2007_Workbook' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook::class, - 'PHPExcel_Writer_Excel2007_Worksheet' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::class, - 'PHPExcel_Writer_Excel2007_WriterPart' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx\WriterPart::class, - 'PHPExcel_CachedObjectStorage_CacheBase' => \PhpOffice\PhpSpreadsheet\Collection\Cells::class, - 'PHPExcel_CalcEngine_CyclicReferenceStack' => \PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack::class, - 'PHPExcel_CalcEngine_Logger' => \PhpOffice\PhpSpreadsheet\Calculation\Engine\Logger::class, - 'PHPExcel_Calculation_Functions' => \PhpOffice\PhpSpreadsheet\Calculation\Functions::class, - 'PHPExcel_Calculation_Function' => \PhpOffice\PhpSpreadsheet\Calculation\Category::class, - 'PHPExcel_Calculation_Database' => \PhpOffice\PhpSpreadsheet\Calculation\Database::class, - 'PHPExcel_Calculation_DateTime' => \PhpOffice\PhpSpreadsheet\Calculation\DateTime::class, - 'PHPExcel_Calculation_Engineering' => \PhpOffice\PhpSpreadsheet\Calculation\Engineering::class, - 'PHPExcel_Calculation_Exception' => \PhpOffice\PhpSpreadsheet\Calculation\Exception::class, - 'PHPExcel_Calculation_ExceptionHandler' => \PhpOffice\PhpSpreadsheet\Calculation\ExceptionHandler::class, - 'PHPExcel_Calculation_Financial' => \PhpOffice\PhpSpreadsheet\Calculation\Financial::class, - 'PHPExcel_Calculation_FormulaParser' => \PhpOffice\PhpSpreadsheet\Calculation\FormulaParser::class, - 'PHPExcel_Calculation_FormulaToken' => \PhpOffice\PhpSpreadsheet\Calculation\FormulaToken::class, - 'PHPExcel_Calculation_Logical' => \PhpOffice\PhpSpreadsheet\Calculation\Logical::class, - 'PHPExcel_Calculation_LookupRef' => \PhpOffice\PhpSpreadsheet\Calculation\LookupRef::class, - 'PHPExcel_Calculation_MathTrig' => \PhpOffice\PhpSpreadsheet\Calculation\MathTrig::class, - 'PHPExcel_Calculation_Statistical' => \PhpOffice\PhpSpreadsheet\Calculation\Statistical::class, - 'PHPExcel_Calculation_TextData' => \PhpOffice\PhpSpreadsheet\Calculation\TextData::class, - 'PHPExcel_Cell_AdvancedValueBinder' => \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class, - 'PHPExcel_Cell_DataType' => \PhpOffice\PhpSpreadsheet\Cell\DataType::class, - 'PHPExcel_Cell_DataValidation' => \PhpOffice\PhpSpreadsheet\Cell\DataValidation::class, - 'PHPExcel_Cell_DefaultValueBinder' => \PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder::class, - 'PHPExcel_Cell_Hyperlink' => \PhpOffice\PhpSpreadsheet\Cell\Hyperlink::class, - 'PHPExcel_Cell_IValueBinder' => \PhpOffice\PhpSpreadsheet\Cell\IValueBinder::class, - 'PHPExcel_Chart_Axis' => \PhpOffice\PhpSpreadsheet\Chart\Axis::class, - 'PHPExcel_Chart_DataSeries' => \PhpOffice\PhpSpreadsheet\Chart\DataSeries::class, - 'PHPExcel_Chart_DataSeriesValues' => \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues::class, - 'PHPExcel_Chart_Exception' => \PhpOffice\PhpSpreadsheet\Chart\Exception::class, - 'PHPExcel_Chart_GridLines' => \PhpOffice\PhpSpreadsheet\Chart\GridLines::class, - 'PHPExcel_Chart_Layout' => \PhpOffice\PhpSpreadsheet\Chart\Layout::class, - 'PHPExcel_Chart_Legend' => \PhpOffice\PhpSpreadsheet\Chart\Legend::class, - 'PHPExcel_Chart_PlotArea' => \PhpOffice\PhpSpreadsheet\Chart\PlotArea::class, - 'PHPExcel_Properties' => \PhpOffice\PhpSpreadsheet\Chart\Properties::class, - 'PHPExcel_Chart_Title' => \PhpOffice\PhpSpreadsheet\Chart\Title::class, - 'PHPExcel_DocumentProperties' => \PhpOffice\PhpSpreadsheet\Document\Properties::class, - 'PHPExcel_DocumentSecurity' => \PhpOffice\PhpSpreadsheet\Document\Security::class, - 'PHPExcel_Helper_HTML' => \PhpOffice\PhpSpreadsheet\Helper\Html::class, - 'PHPExcel_Reader_Abstract' => \PhpOffice\PhpSpreadsheet\Reader\BaseReader::class, - 'PHPExcel_Reader_CSV' => \PhpOffice\PhpSpreadsheet\Reader\Csv::class, - 'PHPExcel_Reader_DefaultReadFilter' => \PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter::class, - 'PHPExcel_Reader_Excel2003XML' => \PhpOffice\PhpSpreadsheet\Reader\Xml::class, - 'PHPExcel_Reader_Exception' => \PhpOffice\PhpSpreadsheet\Reader\Exception::class, - 'PHPExcel_Reader_Gnumeric' => \PhpOffice\PhpSpreadsheet\Reader\Gnumeric::class, - 'PHPExcel_Reader_HTML' => \PhpOffice\PhpSpreadsheet\Reader\Html::class, - 'PHPExcel_Reader_IReadFilter' => \PhpOffice\PhpSpreadsheet\Reader\IReadFilter::class, - 'PHPExcel_Reader_IReader' => \PhpOffice\PhpSpreadsheet\Reader\IReader::class, - 'PHPExcel_Reader_OOCalc' => \PhpOffice\PhpSpreadsheet\Reader\Ods::class, - 'PHPExcel_Reader_SYLK' => \PhpOffice\PhpSpreadsheet\Reader\Slk::class, - 'PHPExcel_Reader_Excel5' => \PhpOffice\PhpSpreadsheet\Reader\Xls::class, - 'PHPExcel_Reader_Excel2007' => \PhpOffice\PhpSpreadsheet\Reader\Xlsx::class, - 'PHPExcel_RichText_ITextElement' => \PhpOffice\PhpSpreadsheet\RichText\ITextElement::class, - 'PHPExcel_RichText_Run' => \PhpOffice\PhpSpreadsheet\RichText\Run::class, - 'PHPExcel_RichText_TextElement' => \PhpOffice\PhpSpreadsheet\RichText\TextElement::class, - 'PHPExcel_Shared_CodePage' => \PhpOffice\PhpSpreadsheet\Shared\CodePage::class, - 'PHPExcel_Shared_Date' => \PhpOffice\PhpSpreadsheet\Shared\Date::class, - 'PHPExcel_Shared_Drawing' => \PhpOffice\PhpSpreadsheet\Shared\Drawing::class, - 'PHPExcel_Shared_Escher' => \PhpOffice\PhpSpreadsheet\Shared\Escher::class, - 'PHPExcel_Shared_File' => \PhpOffice\PhpSpreadsheet\Shared\File::class, - 'PHPExcel_Shared_Font' => \PhpOffice\PhpSpreadsheet\Shared\Font::class, - 'PHPExcel_Shared_OLE' => \PhpOffice\PhpSpreadsheet\Shared\OLE::class, - 'PHPExcel_Shared_OLERead' => \PhpOffice\PhpSpreadsheet\Shared\OLERead::class, - 'PHPExcel_Shared_PasswordHasher' => \PhpOffice\PhpSpreadsheet\Shared\PasswordHasher::class, - 'PHPExcel_Shared_String' => \PhpOffice\PhpSpreadsheet\Shared\StringHelper::class, - 'PHPExcel_Shared_TimeZone' => \PhpOffice\PhpSpreadsheet\Shared\TimeZone::class, - 'PHPExcel_Shared_XMLWriter' => \PhpOffice\PhpSpreadsheet\Shared\XMLWriter::class, - 'PHPExcel_Shared_Excel5' => \PhpOffice\PhpSpreadsheet\Shared\Xls::class, - 'PHPExcel_Style_Alignment' => \PhpOffice\PhpSpreadsheet\Style\Alignment::class, - 'PHPExcel_Style_Border' => \PhpOffice\PhpSpreadsheet\Style\Border::class, - 'PHPExcel_Style_Borders' => \PhpOffice\PhpSpreadsheet\Style\Borders::class, - 'PHPExcel_Style_Color' => \PhpOffice\PhpSpreadsheet\Style\Color::class, - 'PHPExcel_Style_Conditional' => \PhpOffice\PhpSpreadsheet\Style\Conditional::class, - 'PHPExcel_Style_Fill' => \PhpOffice\PhpSpreadsheet\Style\Fill::class, - 'PHPExcel_Style_Font' => \PhpOffice\PhpSpreadsheet\Style\Font::class, - 'PHPExcel_Style_NumberFormat' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::class, - 'PHPExcel_Style_Protection' => \PhpOffice\PhpSpreadsheet\Style\Protection::class, - 'PHPExcel_Style_Supervisor' => \PhpOffice\PhpSpreadsheet\Style\Supervisor::class, - 'PHPExcel_Worksheet_AutoFilter' => \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter::class, - 'PHPExcel_Worksheet_BaseDrawing' => \PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing::class, - 'PHPExcel_Worksheet_CellIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\CellIterator::class, - 'PHPExcel_Worksheet_Column' => \PhpOffice\PhpSpreadsheet\Worksheet\Column::class, - 'PHPExcel_Worksheet_ColumnCellIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\ColumnCellIterator::class, - 'PHPExcel_Worksheet_ColumnDimension' => \PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension::class, - 'PHPExcel_Worksheet_ColumnIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\ColumnIterator::class, - 'PHPExcel_Worksheet_Drawing' => \PhpOffice\PhpSpreadsheet\Worksheet\Drawing::class, - 'PHPExcel_Worksheet_HeaderFooter' => \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter::class, - 'PHPExcel_Worksheet_HeaderFooterDrawing' => \PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing::class, - 'PHPExcel_WorksheetIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\Iterator::class, - 'PHPExcel_Worksheet_MemoryDrawing' => \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing::class, - 'PHPExcel_Worksheet_PageMargins' => \PhpOffice\PhpSpreadsheet\Worksheet\PageMargins::class, - 'PHPExcel_Worksheet_PageSetup' => \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::class, - 'PHPExcel_Worksheet_Protection' => \PhpOffice\PhpSpreadsheet\Worksheet\Protection::class, - 'PHPExcel_Worksheet_Row' => \PhpOffice\PhpSpreadsheet\Worksheet\Row::class, - 'PHPExcel_Worksheet_RowCellIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator::class, - 'PHPExcel_Worksheet_RowDimension' => \PhpOffice\PhpSpreadsheet\Worksheet\RowDimension::class, - 'PHPExcel_Worksheet_RowIterator' => \PhpOffice\PhpSpreadsheet\Worksheet\RowIterator::class, - 'PHPExcel_Worksheet_SheetView' => \PhpOffice\PhpSpreadsheet\Worksheet\SheetView::class, - 'PHPExcel_Writer_Abstract' => \PhpOffice\PhpSpreadsheet\Writer\BaseWriter::class, - 'PHPExcel_Writer_CSV' => \PhpOffice\PhpSpreadsheet\Writer\Csv::class, - 'PHPExcel_Writer_Exception' => \PhpOffice\PhpSpreadsheet\Writer\Exception::class, - 'PHPExcel_Writer_HTML' => \PhpOffice\PhpSpreadsheet\Writer\Html::class, - 'PHPExcel_Writer_IWriter' => \PhpOffice\PhpSpreadsheet\Writer\IWriter::class, - 'PHPExcel_Writer_OpenDocument' => \PhpOffice\PhpSpreadsheet\Writer\Ods::class, - 'PHPExcel_Writer_PDF' => \PhpOffice\PhpSpreadsheet\Writer\Pdf::class, - 'PHPExcel_Writer_Excel5' => \PhpOffice\PhpSpreadsheet\Writer\Xls::class, - 'PHPExcel_Writer_Excel2007' => \PhpOffice\PhpSpreadsheet\Writer\Xlsx::class, - 'PHPExcel_CachedObjectStorageFactory' => \PhpOffice\PhpSpreadsheet\Collection\CellsFactory::class, - 'PHPExcel_Calculation' => \PhpOffice\PhpSpreadsheet\Calculation\Calculation::class, - 'PHPExcel_Cell' => \PhpOffice\PhpSpreadsheet\Cell\Cell::class, - 'PHPExcel_Chart' => \PhpOffice\PhpSpreadsheet\Chart\Chart::class, - 'PHPExcel_Comment' => \PhpOffice\PhpSpreadsheet\Comment::class, - 'PHPExcel_Exception' => \PhpOffice\PhpSpreadsheet\Exception::class, - 'PHPExcel_HashTable' => \PhpOffice\PhpSpreadsheet\HashTable::class, - 'PHPExcel_IComparable' => \PhpOffice\PhpSpreadsheet\IComparable::class, - 'PHPExcel_IOFactory' => \PhpOffice\PhpSpreadsheet\IOFactory::class, - 'PHPExcel_NamedRange' => \PhpOffice\PhpSpreadsheet\NamedRange::class, - 'PHPExcel_ReferenceHelper' => \PhpOffice\PhpSpreadsheet\ReferenceHelper::class, - 'PHPExcel_RichText' => \PhpOffice\PhpSpreadsheet\RichText\RichText::class, - 'PHPExcel_Settings' => \PhpOffice\PhpSpreadsheet\Settings::class, - 'PHPExcel_Style' => \PhpOffice\PhpSpreadsheet\Style\Style::class, - 'PHPExcel_Worksheet' => \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::class, - ]; - - $methods = [ - 'MINUTEOFHOUR' => 'MINUTE', - 'SECONDOFMINUTE' => 'SECOND', - 'DAYOFWEEK' => 'WEEKDAY', - 'WEEKOFYEAR' => 'WEEKNUM', - 'ExcelToPHPObject' => 'excelToDateTimeObject', - 'ExcelToPHP' => 'excelToTimestamp', - 'FormattedPHPToExcel' => 'formattedPHPToExcel', - 'Cell::absoluteCoordinate' => 'Coordinate::absoluteCoordinate', - 'Cell::absoluteReference' => 'Coordinate::absoluteReference', - 'Cell::buildRange' => 'Coordinate::buildRange', - 'Cell::columnIndexFromString' => 'Coordinate::columnIndexFromString', - 'Cell::coordinateFromString' => 'Coordinate::coordinateFromString', - 'Cell::extractAllCellReferencesInRange' => 'Coordinate::extractAllCellReferencesInRange', - 'Cell::getRangeBoundaries' => 'Coordinate::getRangeBoundaries', - 'Cell::mergeRangesInCollection' => 'Coordinate::mergeRangesInCollection', - 'Cell::rangeBoundaries' => 'Coordinate::rangeBoundaries', - 'Cell::rangeDimension' => 'Coordinate::rangeDimension', - 'Cell::splitRange' => 'Coordinate::splitRange', - 'Cell::stringFromColumnIndex' => 'Coordinate::stringFromColumnIndex', - ]; - - // Keep '\' prefix for class names - $prefixedClasses = []; - foreach ($classes as $key => &$value) { - $value = str_replace('PhpOffice\\', '\\PhpOffice\\', $value); - $prefixedClasses['\\' . $key] = $value; - } - $mapping = $prefixedClasses + $classes + $methods; - - return $mapping; - } - - /** - * Search in all files in given directory. - * - * @param string $path - */ - private function recursiveReplace($path) - { - $patterns = [ - '/*.md', - '/*.txt', - '/*.TXT', - '/*.php', - '/*.phpt', - '/*.php3', - '/*.php4', - '/*.php5', - '/*.phtml', - ]; - - foreach ($patterns as $pattern) { - foreach (glob($path . $pattern) as $file) { - if (strpos($path, '/vendor/') !== false) { - echo $file . " skipped\n"; - - continue; - } - $original = file_get_contents($file); - $converted = $this->replace($original); - - if ($original !== $converted) { - echo $file . " converted\n"; - file_put_contents($file, $converted); - } - } - } - - // Do the recursion in subdirectory - foreach (glob($path . '/*', GLOB_ONLYDIR) as $subpath) { - if (strpos($subpath, $path . '/') === 0) { - $this->recursiveReplace($subpath); - } - } - } - - public function migrate() - { - $path = realpath(getcwd()); - echo 'This will search and replace recursively in ' . $path . PHP_EOL; - echo 'You MUST backup your files first, or you risk losing data.' . PHP_EOL; - echo 'Are you sure ? (y/n)'; - - $confirm = fread(STDIN, 1); - if ($confirm === 'y') { - $this->recursiveReplace($path); - } - } - - /** - * Migrate the given code from PHPExcel to PhpSpreadsheet. - * - * @param string $original - * - * @return string - */ - public function replace($original) - { - $converted = str_replace($this->from, $this->to, $original); - - // The string "PHPExcel" gets special treatment because of how common it might be. - // This regex requires a word boundary around the string, and it can't be - // preceded by $ or -> (goal is to filter out cases where a variable is named $PHPExcel or similar) - $converted = preg_replace('~(?)(\b|\\\\)PHPExcel\b~', '\\' . \PhpOffice\PhpSpreadsheet\Spreadsheet::class, $converted); - - return $converted; - } -} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php index e199c807c0d..a91b195e24b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php @@ -11,6 +11,7 @@ use RecursiveIteratorIterator; use RecursiveRegexIterator; use ReflectionClass; use RegexIterator; +use RuntimeException; /** * Helper class to be used in sample code. @@ -106,11 +107,10 @@ class Sample /** * Write documents. * - * @param Spreadsheet $spreadsheet * @param string $filename * @param string[] $writers */ - public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']) + public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']): void { // Set active sheet index to the first sheet, so Excel opens this as the first sheet $spreadsheet->setActiveSheetIndex(0); @@ -142,7 +142,7 @@ class Sample $tempFolder = sys_get_temp_dir() . '/phpspreadsheet'; if (!is_dir($tempFolder)) { if (!mkdir($tempFolder) && !is_dir($tempFolder)) { - throw new \RuntimeException(sprintf('Directory "%s" was not created', $tempFolder)); + throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder)); } } @@ -179,7 +179,7 @@ class Sample return $temporaryFilename . '.' . $extension; } - public function log($message) + public function log($message): void { $eol = $this->isCli() ? PHP_EOL : '
      '; echo date('H:i:s ') . $message . $eol; @@ -188,7 +188,7 @@ class Sample /** * Log ending notes. */ - public function logEndingNotes() + public function logEndingNotes(): void { // Do not show execution time for index $this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB'); @@ -197,11 +197,10 @@ class Sample /** * Log a line about the write operation. * - * @param IWriter $writer * @param string $path * @param float $callStartTime */ - public function logWrite(IWriter $writer, $path, $callStartTime) + public function logWrite(IWriter $writer, $path, $callStartTime): void { $callEndTime = microtime(true); $callTime = $callEndTime - $callStartTime; @@ -219,7 +218,7 @@ class Sample * @param string $path * @param float $callStartTime */ - public function logRead($format, $path, $callStartTime) + public function logRead($format, $path, $callStartTime): void { $callEndTime = microtime(true); $callTime = $callEndTime - $callStartTime; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php index 4266ea546ad..ab04e9698ef 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php @@ -37,11 +37,8 @@ abstract class IOFactory /** * Create Writer\IWriter. * - * @param Spreadsheet $spreadsheet * @param string $writerType Example: Xlsx * - * @throws Writer\Exception - * * @return Writer\IWriter */ public static function createWriter(Spreadsheet $spreadsheet, $writerType) @@ -61,8 +58,6 @@ abstract class IOFactory * * @param string $readerType Example: Xlsx * - * @throws Reader\Exception - * * @return Reader\IReader */ public static function createReader($readerType) @@ -82,8 +77,6 @@ abstract class IOFactory * * @param string $pFilename The name of the spreadsheet file * - * @throws Reader\Exception - * * @return Spreadsheet */ public static function load($pFilename) @@ -98,8 +91,6 @@ abstract class IOFactory * * @param string $pFilename The name of the spreadsheet file to identify * - * @throws Reader\Exception - * * @return string */ public static function identify($pFilename) @@ -117,8 +108,6 @@ abstract class IOFactory * * @param string $filename The name of the spreadsheet file * - * @throws Reader\Exception - * * @return Reader\IReader */ public static function createReaderForFile($filename) @@ -202,7 +191,7 @@ abstract class IOFactory * @param string $writerType * @param string $writerClass */ - public static function registerWriter($writerType, $writerClass) + public static function registerWriter($writerType, $writerClass): void { if (!is_a($writerClass, Writer\IWriter::class, true)) { throw new Writer\Exception('Registered writers must implement ' . Writer\IWriter::class); @@ -217,7 +206,7 @@ abstract class IOFactory * @param string $readerType * @param string $readerClass */ - public static function registerReader($readerType, $readerClass) + public static function registerReader($readerType, $readerClass): void { if (!is_a($readerClass, Reader\IReader::class, true)) { throw new Reader\Exception('Registered readers must implement ' . Reader\IReader::class); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php new file mode 100644 index 00000000000..eeddbbcb99a --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedFormula.php @@ -0,0 +1,45 @@ +value; + } + + /** + * Set the formula value. + */ + public function setFormula(string $formula): self + { + if (!empty($formula)) { + $this->value = $formula; + } + + return $this; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php index 1f94d5a4ed2..db9c5f12178 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php @@ -2,239 +2,54 @@ namespace PhpOffice\PhpSpreadsheet; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -class NamedRange +class NamedRange extends DefinedName { /** - * Range name. - * - * @var string + * Create a new Named Range. */ - private $name; - - /** - * Worksheet on which the named range can be resolved. - * - * @var Worksheet - */ - private $worksheet; - - /** - * Range of the referenced cells. - * - * @var string - */ - private $range; - - /** - * Is the named range local? (i.e. can only be used on $this->worksheet). - * - * @var bool - */ - private $localOnly; - - /** - * Scope. - * - * @var Worksheet - */ - private $scope; - - /** - * Create a new NamedRange. - * - * @param string $pName - * @param Worksheet $pWorksheet - * @param string $pRange - * @param bool $pLocalOnly - * @param null|Worksheet $pScope Scope. Only applies when $pLocalOnly = true. Null for global scope. - * - * @throws Exception - */ - public function __construct($pName, Worksheet $pWorksheet, $pRange = 'A1', $pLocalOnly = false, $pScope = null) - { - // Validate data - if (($pName === null) || ($pWorksheet === null) || ($pRange === null)) { - throw new Exception('Parameters can not be null.'); + public function __construct( + string $name, + ?Worksheet $worksheet = null, + string $range = 'A1', + bool $localOnly = false, + ?Worksheet $scope = null + ) { + if ($worksheet === null && $scope === null) { + throw new Exception('You must specify a worksheet or a scope for a Named Range'); } - - // Set local members - $this->name = $pName; - $this->worksheet = $pWorksheet; - $this->range = $pRange; - $this->localOnly = $pLocalOnly; - $this->scope = ($pLocalOnly == true) ? (($pScope == null) ? $pWorksheet : $pScope) : null; + parent::__construct($name, $worksheet, $range, $localOnly, $scope); } /** - * Get name. - * - * @return string + * Get the range value. */ - public function getName() + public function getRange(): string { - return $this->name; + return $this->value; } /** - * Set name. - * - * @param string $value - * - * @return NamedRange + * Set the range value. */ - public function setName($value) + public function setRange(string $range): self { - if ($value !== null) { - // Old title - $oldTitle = $this->name; - - // Re-attach - if ($this->worksheet !== null) { - $this->worksheet->getParent()->removeNamedRange($this->name, $this->worksheet); - } - $this->name = $value; - - if ($this->worksheet !== null) { - $this->worksheet->getParent()->addNamedRange($this); - } - - // New title - $newTitle = $this->name; - ReferenceHelper::getInstance()->updateNamedFormulas($this->worksheet->getParent(), $oldTitle, $newTitle); + if (!empty($range)) { + $this->value = $range; } return $this; } - /** - * Get worksheet. - * - * @return Worksheet - */ - public function getWorksheet() + public function getCellsInRange(): array { - return $this->worksheet; - } - - /** - * Set worksheet. - * - * @param Worksheet $value - * - * @return NamedRange - */ - public function setWorksheet(Worksheet $value = null) - { - if ($value !== null) { - $this->worksheet = $value; + $range = $this->value; + if (substr($range, 0, 1) === '=') { + $range = substr($range, 1); } - return $this; - } - - /** - * Get range. - * - * @return string - */ - public function getRange() - { - return $this->range; - } - - /** - * Set range. - * - * @param string $value - * - * @return NamedRange - */ - public function setRange($value) - { - if ($value !== null) { - $this->range = $value; - } - - return $this; - } - - /** - * Get localOnly. - * - * @return bool - */ - public function getLocalOnly() - { - return $this->localOnly; - } - - /** - * Set localOnly. - * - * @param bool $value - * - * @return NamedRange - */ - public function setLocalOnly($value) - { - $this->localOnly = $value; - $this->scope = $value ? $this->worksheet : null; - - return $this; - } - - /** - * Get scope. - * - * @return null|Worksheet - */ - public function getScope() - { - return $this->scope; - } - - /** - * Set scope. - * - * @param null|Worksheet $value - * - * @return NamedRange - */ - public function setScope(Worksheet $value = null) - { - $this->scope = $value; - $this->localOnly = $value != null; - - return $this; - } - - /** - * Resolve a named range to a regular cell range. - * - * @param string $pNamedRange Named range - * @param null|Worksheet $pSheet Scope. Use null for global scope - * - * @return NamedRange - */ - public static function resolveRange($pNamedRange, Worksheet $pSheet) - { - return $pSheet->getParent()->getNamedRange($pNamedRange, $pSheet); - } - - /** - * Implement PHP __clone to create a deep clone, not just a shallow copy. - */ - public function __clone() - { - $vars = get_object_vars($this); - foreach ($vars as $key => $value) { - if (is_object($value)) { - $this->$key = clone $value; - } else { - $this->$key = $value; - } - } + return Coordinate::extractAllCellReferencesInRange($range); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php index f7af1557fff..eb0e3ba223c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\Shared\File; @@ -133,28 +134,28 @@ abstract class BaseReader implements IReader public function getSecurityScanner() { - if (property_exists($this, 'securityScanner')) { - return $this->securityScanner; - } - - return null; + return $this->securityScanner; } /** * Open file for reading. * * @param string $pFilename - * - * @throws Exception */ - protected function openFile($pFilename) + protected function openFile($pFilename): void { - File::assertFile($pFilename); + if ($pFilename) { + File::assertFile($pFilename); - // Open file - $this->fileHandle = fopen($pFilename, 'r'); - if ($this->fileHandle === false) { - throw new Exception('Could not open file ' . $pFilename . ' for reading.'); + // Open file + $fileHandle = fopen($pFilename, 'rb'); + } else { + $fileHandle = false; + } + if ($fileHandle !== false) { + $this->fileHandle = $fileHandle; + } else { + throw new ReaderException('Could not open file ' . $pFilename . ' for reading.'); } } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php index 21251913359..1495d102c00 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php @@ -2,12 +2,28 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use InvalidArgumentException; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; class Csv extends BaseReader { + const UTF8_BOM = "\xEF\xBB\xBF"; + const UTF8_BOM_LEN = 3; + const UTF16BE_BOM = "\xfe\xff"; + const UTF16BE_BOM_LEN = 2; + const UTF16BE_LF = "\x00\x0a"; + const UTF16LE_BOM = "\xff\xfe"; + const UTF16LE_BOM_LEN = 2; + const UTF16LE_LF = "\x0a\x00"; + const UTF32BE_BOM = "\x00\x00\xfe\xff"; + const UTF32BE_BOM_LEN = 4; + const UTF32BE_LF = "\x00\x00\x00\x0a"; + const UTF32LE_BOM = "\xff\xfe\x00\x00"; + const UTF32LE_BOM_LEN = 4; + const UTF32LE_LF = "\x0a\x00\x00\x00"; + /** * Input encoding. * @@ -43,13 +59,6 @@ class Csv extends BaseReader */ private $contiguous = false; - /** - * Row counter for loading rows contiguously. - * - * @var int - */ - private $contiguousRow = -1; - /** * The character that can escape the enclosure. * @@ -70,7 +79,7 @@ class Csv extends BaseReader * * @param string $pValue Input encoding, eg: 'UTF-8' * - * @return Csv + * @return $this */ public function setInputEncoding($pValue) { @@ -92,45 +101,19 @@ class Csv extends BaseReader /** * Move filepointer past any BOM marker. */ - protected function skipBOM() + protected function skipBOM(): void { rewind($this->fileHandle); - switch ($this->inputEncoding) { - case 'UTF-8': - fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ? - fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0); - - break; - case 'UTF-16LE': - fgets($this->fileHandle, 3) == "\xFF\xFE" ? - fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0); - - break; - case 'UTF-16BE': - fgets($this->fileHandle, 3) == "\xFE\xFF" ? - fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0); - - break; - case 'UTF-32LE': - fgets($this->fileHandle, 5) == "\xFF\xFE\x00\x00" ? - fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0); - - break; - case 'UTF-32BE': - fgets($this->fileHandle, 5) == "\x00\x00\xFE\xFF" ? - fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0); - - break; - default: - break; + if (fgets($this->fileHandle, self::UTF8_BOM_LEN + 1) !== self::UTF8_BOM) { + rewind($this->fileHandle); } } /** * Identify any separator that is explicitly set in the file. */ - protected function checkSeparator() + protected function checkSeparator(): void { $line = fgets($this->fileHandle); if ($line === false) { @@ -149,7 +132,7 @@ class Csv extends BaseReader /** * Infer the separator if it isn't explicitly set in the file or specified by the user. */ - protected function inferSeparator() + protected function inferSeparator(): void { if ($this->delimiter !== null) { return; @@ -207,7 +190,7 @@ class Csv extends BaseReader $meanSquareDeviations[$delimiter] = array_reduce( $series, function ($sum, $value) use ($median) { - return $sum + pow($value - $median, 2); + return $sum + ($value - $median) ** 2; } ) / count($series); } @@ -236,33 +219,33 @@ class Csv extends BaseReader /** * Get the next full line from the file. * - * @param string $line - * - * @return bool|string + * @return false|string */ - private function getNextLine($line = '') + private function getNextLine() { - // Get the next line in the file - $newLine = fgets($this->fileHandle); - - // Return false if there is no next line - if ($newLine === false) { - return false; - } - - // Add the new line to the line passed in - $line = $line . $newLine; - - // Drop everything that is enclosed to avoid counting false positives in enclosures - $enclosure = '(?escapeCharacter, '/') . ')' + $line = ''; + $enclosure = ($this->escapeCharacter === '' ? '' + : ('(?escapeCharacter, '/') . ')')) . preg_quote($this->enclosure, '/'); - $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line); - // See if we have any enclosures left in the line - // if we still have an enclosure then we need to read the next line as well - if (preg_match('/(' . $enclosure . ')/', $line) > 0) { - $line = $this->getNextLine($line); - } + do { + // Get the next line in the file + $newLine = fgets($this->fileHandle); + + // Return false if there is no next line + if ($newLine === false) { + return false; + } + + // Add the new line to the line passed in + $line = $line . $newLine; + + // Drop everything that is enclosed to avoid counting false positives in enclosures + $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line); + + // See if we have any enclosures left in the line + // if we still have an enclosure then we need to read the next line as well + } while (preg_match('/(' . $enclosure . ')/', $line) > 0); return $line; } @@ -272,17 +255,12 @@ class Csv extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetInfo($pFilename) { // Open file - if (!$this->canRead($pFilename)) { - throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); - } - $this->openFile($pFilename); + $this->openFileOrMemory($pFilename); $fileHandle = $this->fileHandle; // Skip BOM, if any @@ -317,8 +295,6 @@ class Csv extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -330,13 +306,28 @@ class Csv extends BaseReader return $this->loadIntoExisting($pFilename, $spreadsheet); } + private function openFileOrMemory($pFilename): void + { + // Open file + $fhandle = $this->canRead($pFilename); + if (!$fhandle) { + throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); + } + $this->openFile($pFilename); + if ($this->inputEncoding !== 'UTF-8') { + fclose($this->fileHandle); + $entireFile = file_get_contents($pFilename); + $this->fileHandle = fopen('php://memory', 'r+b'); + $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding); + fwrite($this->fileHandle, $data); + $this->skipBOM(); + } + } + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. * * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception * * @return Spreadsheet */ @@ -346,10 +337,7 @@ class Csv extends BaseReader ini_set('auto_detect_line_endings', true); // Open file - if (!$this->canRead($pFilename)) { - throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); - } - $this->openFile($pFilename); + $this->openFileOrMemory($pFilename); $fileHandle = $this->fileHandle; // Skip BOM, if any @@ -365,22 +353,24 @@ class Csv extends BaseReader // Set our starting row based on whether we're in contiguous mode or not $currentRow = 1; - if ($this->contiguous) { - $currentRow = ($this->contiguousRow == -1) ? $sheet->getHighestRow() : $this->contiguousRow; - } + $outRow = 0; // Loop through each line of the file in turn while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) { + $noOutputYet = true; $columnLetter = 'A'; foreach ($rowData as $rowDatum) { if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) { - // Convert encoding if necessary - if ($this->inputEncoding !== 'UTF-8') { - $rowDatum = StringHelper::convertEncoding($rowDatum, 'UTF-8', $this->inputEncoding); + if ($this->contiguous) { + if ($noOutputYet) { + $noOutputYet = false; + ++$outRow; + } + } else { + $outRow = $currentRow; } - // Set cell value - $sheet->getCell($columnLetter . $currentRow)->setValue($rowDatum); + $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum); } ++$columnLetter; } @@ -390,10 +380,6 @@ class Csv extends BaseReader // Close file fclose($fileHandle); - if ($this->contiguous) { - $this->contiguousRow = $currentRow; - } - ini_set('auto_detect_line_endings', $lineEnding); // Return @@ -415,7 +401,7 @@ class Csv extends BaseReader * * @param string $delimiter Delimiter, eg: ',' * - * @return CSV + * @return $this */ public function setDelimiter($delimiter) { @@ -439,7 +425,7 @@ class Csv extends BaseReader * * @param string $enclosure Enclosure, defaults to " * - * @return CSV + * @return $this */ public function setEnclosure($enclosure) { @@ -466,7 +452,7 @@ class Csv extends BaseReader * * @param int $pValue Sheet index * - * @return CSV + * @return $this */ public function setSheetIndex($pValue) { @@ -480,14 +466,11 @@ class Csv extends BaseReader * * @param bool $contiguous * - * @return Csv + * @return $this */ public function setContiguous($contiguous) { $this->contiguous = (bool) $contiguous; - if (!$contiguous) { - $this->contiguousRow = -1; - } return $this; } @@ -538,7 +521,7 @@ class Csv extends BaseReader // Check if file exists try { $this->openFile($pFilename); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { return false; } @@ -553,6 +536,7 @@ class Csv extends BaseReader // Attempt to guess mimetype $type = mime_content_type($pFilename); $supportedTypes = [ + 'application/csv', 'text/csv', 'text/plain', 'inode/x-empty', @@ -560,4 +544,63 @@ class Csv extends BaseReader return in_array($type, $supportedTypes, true); } + + private static function guessEncodingTestNoBom(string &$encoding, string &$contents, string $compare, string $setEncoding): void + { + if ($encoding === '') { + $pos = strpos($contents, $compare); + if ($pos !== false && $pos % strlen($compare) === 0) { + $encoding = $setEncoding; + } + } + } + + private static function guessEncodingNoBom(string $filename): string + { + $encoding = ''; + $contents = file_get_contents($filename); + self::guessEncodingTestNoBom($encoding, $contents, self::UTF32BE_LF, 'UTF-32BE'); + self::guessEncodingTestNoBom($encoding, $contents, self::UTF32LE_LF, 'UTF-32LE'); + self::guessEncodingTestNoBom($encoding, $contents, self::UTF16BE_LF, 'UTF-16BE'); + self::guessEncodingTestNoBom($encoding, $contents, self::UTF16LE_LF, 'UTF-16LE'); + if ($encoding === '' && preg_match('//u', $contents) === 1) { + $encoding = 'UTF-8'; + } + + return $encoding; + } + + private static function guessEncodingTestBom(string &$encoding, string $first4, string $compare, string $setEncoding): void + { + if ($encoding === '') { + if ($compare === substr($first4, 0, strlen($compare))) { + $encoding = $setEncoding; + } + } + } + + private static function guessEncodingBom(string $filename): string + { + $encoding = ''; + $first4 = file_get_contents($filename, false, null, 0, 4); + if ($first4 !== false) { + self::guessEncodingTestBom($encoding, $first4, self::UTF8_BOM, 'UTF-8'); + self::guessEncodingTestBom($encoding, $first4, self::UTF16BE_BOM, 'UTF-16BE'); + self::guessEncodingTestBom($encoding, $first4, self::UTF32BE_BOM, 'UTF-32BE'); + self::guessEncodingTestBom($encoding, $first4, self::UTF32LE_BOM, 'UTF-32LE'); + self::guessEncodingTestBom($encoding, $first4, self::UTF16LE_BOM, 'UTF-16LE'); + } + + return $encoding; + } + + public static function guessEncoding(string $filename, string $dflt = 'CP1252'): string + { + $encoding = self::guessEncodingBom($filename); + if ($encoding === '') { + $encoding = self::guessEncodingNoBom($filename); + } + + return ($encoding === '') ? $dflt : $encoding; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php index 44ab701d133..dc921c1e446 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -4,7 +4,8 @@ namespace PhpOffice\PhpSpreadsheet\Reader; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; -use PhpOffice\PhpSpreadsheet\NamedRange; +use PhpOffice\PhpSpreadsheet\DefinedName; +use PhpOffice\PhpSpreadsheet\Reader\Gnumeric\PageSetup; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; @@ -18,10 +19,13 @@ use PhpOffice\PhpSpreadsheet\Style\Borders; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; use XMLReader; class Gnumeric extends BaseReader { + private const UOM_CONVERSION_POINTS_TO_CENTIMETERS = 0.03527777778; + /** * Shared Expressions. * @@ -29,8 +33,23 @@ class Gnumeric extends BaseReader */ private $expressions = []; + /** + * Spreadsheet shared across all functions. + * + * @var Spreadsheet + */ + private $spreadsheet; + private $referenceHelper; + /** + * Namespace shared across all functions. + * It is 'gnm', except for really old sheets which use 'gmr'. + * + * @var string + */ + private $gnm = 'gnm'; + /** * Create a new Gnumeric. */ @@ -46,8 +65,6 @@ class Gnumeric extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return bool */ public function canRead($pFilename) @@ -55,18 +72,22 @@ class Gnumeric extends BaseReader File::assertFile($pFilename); // Check if gzlib functions are available - if (!function_exists('gzread')) { - throw new Exception('gzlib library is not enabled'); + $data = ''; + if (function_exists('gzread')) { + // Read signature data (first 3 bytes) + $fh = fopen($pFilename, 'rb'); + $data = fread($fh, 2); + fclose($fh); } - // Read signature data (first 3 bytes) - $fh = fopen($pFilename, 'r'); - $data = fread($fh, 2); - fclose($fh); - return $data == chr(0x1F) . chr(0x8B); } + private static function matchXml(string $name, string $field): bool + { + return 1 === preg_match("/^(gnm|gmr):$field$/", $name); + } + /** * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object. * @@ -84,10 +105,10 @@ class Gnumeric extends BaseReader $worksheetNames = []; while ($xml->read()) { - if ($xml->name == 'gnm:SheetName' && $xml->nodeType == XMLReader::ELEMENT) { + if (self::matchXml($xml->name, 'SheetName') && $xml->nodeType == XMLReader::ELEMENT) { $xml->read(); // Move onto the value node $worksheetNames[] = (string) $xml->value; - } elseif ($xml->name == 'gnm:Sheets') { + } elseif (self::matchXml($xml->name, 'Sheets')) { // break out of the loop once we've got our sheet names rather than parse the entire file break; } @@ -113,7 +134,7 @@ class Gnumeric extends BaseReader $worksheetInfo = []; while ($xml->read()) { - if ($xml->name == 'gnm:Sheet' && $xml->nodeType == XMLReader::ELEMENT) { + if (self::matchXml($xml->name, 'Sheet') && $xml->nodeType == XMLReader::ELEMENT) { $tmpInfo = [ 'worksheetName' => '', 'lastColumnLetter' => 'A', @@ -123,18 +144,20 @@ class Gnumeric extends BaseReader ]; while ($xml->read()) { - if ($xml->name == 'gnm:Name' && $xml->nodeType == XMLReader::ELEMENT) { - $xml->read(); // Move onto the value node - $tmpInfo['worksheetName'] = (string) $xml->value; - } elseif ($xml->name == 'gnm:MaxCol' && $xml->nodeType == XMLReader::ELEMENT) { - $xml->read(); // Move onto the value node - $tmpInfo['lastColumnIndex'] = (int) $xml->value; - $tmpInfo['totalColumns'] = (int) $xml->value + 1; - } elseif ($xml->name == 'gnm:MaxRow' && $xml->nodeType == XMLReader::ELEMENT) { - $xml->read(); // Move onto the value node - $tmpInfo['totalRows'] = (int) $xml->value + 1; + if ($xml->nodeType == XMLReader::ELEMENT) { + if (self::matchXml($xml->name, 'Name')) { + $xml->read(); // Move onto the value node + $tmpInfo['worksheetName'] = (string) $xml->value; + } elseif (self::matchXml($xml->name, 'MaxCol')) { + $xml->read(); // Move onto the value node + $tmpInfo['lastColumnIndex'] = (int) $xml->value; + $tmpInfo['totalColumns'] = (int) $xml->value + 1; + } elseif (self::matchXml($xml->name, 'MaxRow')) { + $xml->read(); // Move onto the value node + $tmpInfo['totalRows'] = (int) $xml->value + 1; - break; + break; + } } } $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); @@ -164,47 +187,200 @@ class Gnumeric extends BaseReader return $data; } - /** - * Loads Spreadsheet from file. - * - * @param string $pFilename - * - * @throws Exception - * - * @return Spreadsheet - */ - public function load($pFilename) - { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + private static $mappings = [ + 'borderStyle' => [ + '0' => Border::BORDER_NONE, + '1' => Border::BORDER_THIN, + '2' => Border::BORDER_MEDIUM, + '3' => Border::BORDER_SLANTDASHDOT, + '4' => Border::BORDER_DASHED, + '5' => Border::BORDER_THICK, + '6' => Border::BORDER_DOUBLE, + '7' => Border::BORDER_DOTTED, + '8' => Border::BORDER_MEDIUMDASHED, + '9' => Border::BORDER_DASHDOT, + '10' => Border::BORDER_MEDIUMDASHDOT, + '11' => Border::BORDER_DASHDOTDOT, + '12' => Border::BORDER_MEDIUMDASHDOTDOT, + '13' => Border::BORDER_MEDIUMDASHDOTDOT, + ], + 'dataType' => [ + '10' => DataType::TYPE_NULL, + '20' => DataType::TYPE_BOOL, + '30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel + '40' => DataType::TYPE_NUMERIC, // Float + '50' => DataType::TYPE_ERROR, + '60' => DataType::TYPE_STRING, + //'70': // Cell Range + //'80': // Array + ], + 'fillType' => [ + '1' => Fill::FILL_SOLID, + '2' => Fill::FILL_PATTERN_DARKGRAY, + '3' => Fill::FILL_PATTERN_MEDIUMGRAY, + '4' => Fill::FILL_PATTERN_LIGHTGRAY, + '5' => Fill::FILL_PATTERN_GRAY125, + '6' => Fill::FILL_PATTERN_GRAY0625, + '7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe + '8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe + '9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe + '10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe + '11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch + '12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch + '13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL, + '14' => Fill::FILL_PATTERN_LIGHTVERTICAL, + '15' => Fill::FILL_PATTERN_LIGHTUP, + '16' => Fill::FILL_PATTERN_LIGHTDOWN, + '17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch + '18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch + ], + 'horizontal' => [ + '1' => Alignment::HORIZONTAL_GENERAL, + '2' => Alignment::HORIZONTAL_LEFT, + '4' => Alignment::HORIZONTAL_RIGHT, + '8' => Alignment::HORIZONTAL_CENTER, + '16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, + '32' => Alignment::HORIZONTAL_JUSTIFY, + '64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, + ], + 'underline' => [ + '1' => Font::UNDERLINE_SINGLE, + '2' => Font::UNDERLINE_DOUBLE, + '3' => Font::UNDERLINE_SINGLEACCOUNTING, + '4' => Font::UNDERLINE_DOUBLEACCOUNTING, + ], + 'vertical' => [ + '1' => Alignment::VERTICAL_TOP, + '2' => Alignment::VERTICAL_BOTTOM, + '4' => Alignment::VERTICAL_CENTER, + '8' => Alignment::VERTICAL_JUSTIFY, + ], + ]; - // Load into this instance - return $this->loadIntoExisting($pFilename, $spreadsheet); + public static function gnumericMappings(): array + { + return self::$mappings; } - /** - * Loads from file into Spreadsheet instance. - * - * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception - * - * @return Spreadsheet - */ - public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) + private function docPropertiesOld(SimpleXMLElement $gnmXML): void { - File::assertFile($pFilename); + $docProps = $this->spreadsheet->getProperties(); + foreach ($gnmXML->Summary->Item as $summaryItem) { + $propertyName = $summaryItem->name; + $propertyValue = $summaryItem->{'val-string'}; + switch ($propertyName) { + case 'title': + $docProps->setTitle(trim($propertyValue)); - $gFileData = $this->gzfileGetContents($pFilename); + break; + case 'comments': + $docProps->setDescription(trim($propertyValue)); - $xml = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions()); - $namespacesMeta = $xml->getNamespaces(true); + break; + case 'keywords': + $docProps->setKeywords(trim($propertyValue)); - $gnmXML = $xml->children($namespacesMeta['gnm']); + break; + case 'category': + $docProps->setCategory(trim($propertyValue)); - $docProps = $spreadsheet->getProperties(); - // Document Properties are held differently, depending on the version of Gnumeric + break; + case 'manager': + $docProps->setManager(trim($propertyValue)); + + break; + case 'author': + $docProps->setCreator(trim($propertyValue)); + $docProps->setLastModifiedBy(trim($propertyValue)); + + break; + case 'company': + $docProps->setCompany(trim($propertyValue)); + + break; + } + } + } + + private function docPropertiesDC(SimpleXMLElement $officePropertyDC): void + { + $docProps = $this->spreadsheet->getProperties(); + foreach ($officePropertyDC as $propertyName => $propertyValue) { + $propertyValue = trim((string) $propertyValue); + switch ($propertyName) { + case 'title': + $docProps->setTitle($propertyValue); + + break; + case 'subject': + $docProps->setSubject($propertyValue); + + break; + case 'creator': + $docProps->setCreator($propertyValue); + $docProps->setLastModifiedBy($propertyValue); + + break; + case 'date': + $creationDate = strtotime($propertyValue); + $docProps->setCreated($creationDate); + $docProps->setModified($creationDate); + + break; + case 'description': + $docProps->setDescription($propertyValue); + + break; + } + } + } + + private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta, array $namespacesMeta): void + { + $docProps = $this->spreadsheet->getProperties(); + foreach ($officePropertyMeta as $propertyName => $propertyValue) { + $attributes = $propertyValue->attributes($namespacesMeta['meta']); + $propertyValue = trim((string) $propertyValue); + switch ($propertyName) { + case 'keyword': + $docProps->setKeywords($propertyValue); + + break; + case 'initial-creator': + $docProps->setCreator($propertyValue); + $docProps->setLastModifiedBy($propertyValue); + + break; + case 'creation-date': + $creationDate = strtotime($propertyValue); + $docProps->setCreated($creationDate); + $docProps->setModified($creationDate); + + break; + case 'user-defined': + [, $attrName] = explode(':', $attributes['name']); + switch ($attrName) { + case 'publisher': + $docProps->setCompany($propertyValue); + + break; + case 'category': + $docProps->setCategory($propertyValue); + + break; + case 'manager': + $docProps->setManager($propertyValue); + + break; + } + + break; + } + } + } + + private function docProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML, array $namespacesMeta): void + { if (isset($namespacesMeta['office'])) { $officeXML = $xml->children($namespacesMeta['office']); $officeDocXML = $officeXML->{'document-meta'}; @@ -215,115 +391,66 @@ class Gnumeric extends BaseReader if (isset($namespacesMeta['dc'])) { $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']); } - foreach ($officePropertyDC as $propertyName => $propertyValue) { - $propertyValue = (string) $propertyValue; - switch ($propertyName) { - case 'title': - $docProps->setTitle(trim($propertyValue)); + $this->docPropertiesDC($officePropertyDC); - break; - case 'subject': - $docProps->setSubject(trim($propertyValue)); - - break; - case 'creator': - $docProps->setCreator(trim($propertyValue)); - $docProps->setLastModifiedBy(trim($propertyValue)); - - break; - case 'date': - $creationDate = strtotime(trim($propertyValue)); - $docProps->setCreated($creationDate); - $docProps->setModified($creationDate); - - break; - case 'description': - $docProps->setDescription(trim($propertyValue)); - - break; - } - } $officePropertyMeta = []; if (isset($namespacesMeta['meta'])) { $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']); } - foreach ($officePropertyMeta as $propertyName => $propertyValue) { - $attributes = $propertyValue->attributes($namespacesMeta['meta']); - $propertyValue = (string) $propertyValue; - switch ($propertyName) { - case 'keyword': - $docProps->setKeywords(trim($propertyValue)); - - break; - case 'initial-creator': - $docProps->setCreator(trim($propertyValue)); - $docProps->setLastModifiedBy(trim($propertyValue)); - - break; - case 'creation-date': - $creationDate = strtotime(trim($propertyValue)); - $docProps->setCreated($creationDate); - $docProps->setModified($creationDate); - - break; - case 'user-defined': - [, $attrName] = explode(':', $attributes['name']); - switch ($attrName) { - case 'publisher': - $docProps->setCompany(trim($propertyValue)); - - break; - case 'category': - $docProps->setCategory(trim($propertyValue)); - - break; - case 'manager': - $docProps->setManager(trim($propertyValue)); - - break; - } - - break; - } - } + $this->docPropertiesMeta($officePropertyMeta, $namespacesMeta); } } elseif (isset($gnmXML->Summary)) { - foreach ($gnmXML->Summary->Item as $summaryItem) { - $propertyName = $summaryItem->name; - $propertyValue = $summaryItem->{'val-string'}; - switch ($propertyName) { - case 'title': - $docProps->setTitle(trim($propertyValue)); + $this->docPropertiesOld($gnmXML); + } + } - break; - case 'comments': - $docProps->setDescription(trim($propertyValue)); - - break; - case 'keywords': - $docProps->setKeywords(trim($propertyValue)); - - break; - case 'category': - $docProps->setCategory(trim($propertyValue)); - - break; - case 'manager': - $docProps->setManager(trim($propertyValue)); - - break; - case 'author': - $docProps->setCreator(trim($propertyValue)); - $docProps->setLastModifiedBy(trim($propertyValue)); - - break; - case 'company': - $docProps->setCompany(trim($propertyValue)); - - break; + private function processComments(SimpleXMLElement $sheet): void + { + if ((!$this->readDataOnly) && (isset($sheet->Objects))) { + foreach ($sheet->Objects->children($this->gnm, true) as $key => $comment) { + $commentAttributes = $comment->attributes(); + // Only comment objects are handled at the moment + if ($commentAttributes->Text) { + $this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text)); } } } + } + + /** + * Loads Spreadsheet from file. + * + * @param string $pFilename + * + * @return Spreadsheet + */ + public function load($pFilename) + { + // Create new Spreadsheet + $spreadsheet = new Spreadsheet(); + $spreadsheet->removeSheetByIndex(0); + + // Load into this instance + return $this->loadIntoExisting($pFilename, $spreadsheet); + } + + /** + * Loads from file into Spreadsheet instance. + */ + public function loadIntoExisting(string $pFilename, Spreadsheet $spreadsheet): Spreadsheet + { + $this->spreadsheet = $spreadsheet; + File::assertFile($pFilename); + + $gFileData = $this->gzfileGetContents($pFilename); + + $xml2 = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions()); + $xml = ($xml2 !== false) ? $xml2 : new SimpleXMLElement(''); + $namespacesMeta = $xml->getNamespaces(true); + $this->gnm = array_key_exists('gmr', $namespacesMeta) ? 'gmr' : 'gnm'; + + $gnmXML = $xml->children($namespacesMeta[$this->gnm]); + $this->docProperties($xml, $gnmXML, $namespacesMeta); $worksheetID = 0; foreach ($gnmXML->Sheets->Sheet as $sheet) { @@ -335,52 +462,17 @@ class Gnumeric extends BaseReader $maxRow = $maxCol = 0; // Create new Worksheet - $spreadsheet->createSheet(); - $spreadsheet->setActiveSheetIndex($worksheetID); + $this->spreadsheet->createSheet(); + $this->spreadsheet->setActiveSheetIndex($worksheetID); // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula // cells... during the load, all formulae should be correct, and we're simply bringing the worksheet // name in line with the formula, not the reverse - $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); + $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); - if ((!$this->readDataOnly) && (isset($sheet->PrintInformation))) { - if (isset($sheet->PrintInformation->Margins)) { - foreach ($sheet->PrintInformation->Margins->children('gnm', true) as $key => $margin) { - $marginAttributes = $margin->attributes(); - $marginSize = 72 / 100; // Default - switch ($marginAttributes['PrefUnit']) { - case 'mm': - $marginSize = (int) ($marginAttributes['Points']) / 100; - - break; - } - switch ($key) { - case 'top': - $spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize); - - break; - case 'bottom': - $spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize); - - break; - case 'left': - $spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize); - - break; - case 'right': - $spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize); - - break; - case 'header': - $spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize); - - break; - case 'footer': - $spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize); - - break; - } - } - } + if (!$this->readDataOnly) { + (new PageSetup($this->spreadsheet, $this->gnm)) + ->printInformation($sheet) + ->sheetMargins($sheet); } foreach ($sheet->Cells->Cell as $cell) { @@ -427,377 +519,265 @@ class Gnumeric extends BaseReader } $type = DataType::TYPE_FORMULA; } else { - switch ($ValueType) { - case '10': // NULL - $type = DataType::TYPE_NULL; - - break; - case '20': // Boolean - $type = DataType::TYPE_BOOL; - $cell = $cell == 'TRUE'; - - break; - case '30': // Integer - $cell = (int) $cell; - // Excel 2007+ doesn't differentiate between integer and float, so set the value and dropthru to the next (numeric) case - // no break - case '40': // Float - $type = DataType::TYPE_NUMERIC; - - break; - case '50': // Error - $type = DataType::TYPE_ERROR; - - break; - case '60': // String - $type = DataType::TYPE_STRING; - - break; - case '70': // Cell Range - case '80': // Array + $vtype = (string) $ValueType; + if (array_key_exists($vtype, self::$mappings['dataType'])) { + $type = self::$mappings['dataType'][$vtype]; + } + if ($vtype == '20') { // Boolean + $cell = $cell == 'TRUE'; } } - $spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit($cell, $type); + $this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type); } - if ((!$this->readDataOnly) && (isset($sheet->Objects))) { - foreach ($sheet->Objects->children('gnm', true) as $key => $comment) { - $commentAttributes = $comment->attributes(); - // Only comment objects are handled at the moment - if ($commentAttributes->Text) { - $spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text)); - } - } - } + $this->processComments($sheet); + foreach ($sheet->Styles->StyleRegion as $styleRegion) { $styleAttributes = $styleRegion->attributes(); - if (($styleAttributes['startRow'] <= $maxRow) && - ($styleAttributes['startCol'] <= $maxCol)) { + if ( + ($styleAttributes['startRow'] <= $maxRow) && + ($styleAttributes['startCol'] <= $maxCol) + ) { $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); $startRow = $styleAttributes['startRow'] + 1; $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); - $endRow = ($styleAttributes['endRow'] > $maxRow) ? $maxRow : $styleAttributes['endRow']; - $endRow += 1; + + $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; $styleAttributes = $styleRegion->Style->attributes(); + $styleArray = []; // We still set the number format mask for date/time values, even if readDataOnly is true - if ((!$this->readDataOnly) || - (Date::isDateTimeFormatCode((string) $styleAttributes['Format']))) { - $styleArray = []; - $styleArray['numberFormat']['formatCode'] = (string) $styleAttributes['Format']; + $formatCode = (string) $styleAttributes['Format']; + if (Date::isDateTimeFormatCode($formatCode)) { + $styleArray['numberFormat']['formatCode'] = $formatCode; + } + if (!$this->readDataOnly) { // If readDataOnly is false, we set all formatting information - if (!$this->readDataOnly) { - switch ($styleAttributes['HAlign']) { - case '1': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_GENERAL; + $styleArray['numberFormat']['formatCode'] = $formatCode; - break; - case '2': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_LEFT; + self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); + self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); + $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; + $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); + $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; + $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; - break; - case '4': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_RIGHT; + $this->addColors($styleArray, $styleAttributes); - break; - case '8': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER; + $fontAttributes = $styleRegion->Style->Font->attributes(); + $styleArray['font']['name'] = (string) $styleRegion->Style->Font; + $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); + $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; + $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; + $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; + self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); - break; - case '16': - case '64': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER_CONTINUOUS; + switch ($fontAttributes['Script']) { + case '1': + $styleArray['font']['superscript'] = true; - break; - case '32': - $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_JUSTIFY; + break; + case '-1': + $styleArray['font']['subscript'] = true; - break; - } - - switch ($styleAttributes['VAlign']) { - case '1': - $styleArray['alignment']['vertical'] = Alignment::VERTICAL_TOP; - - break; - case '2': - $styleArray['alignment']['vertical'] = Alignment::VERTICAL_BOTTOM; - - break; - case '4': - $styleArray['alignment']['vertical'] = Alignment::VERTICAL_CENTER; - - break; - case '8': - $styleArray['alignment']['vertical'] = Alignment::VERTICAL_JUSTIFY; - - break; - } - - $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; - $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; - $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; - - $RGB = self::parseGnumericColour($styleAttributes['Fore']); - $styleArray['font']['color']['rgb'] = $RGB; - $RGB = self::parseGnumericColour($styleAttributes['Back']); - $shade = $styleAttributes['Shade']; - if (($RGB != '000000') || ($shade != '0')) { - $styleArray['fill']['color']['rgb'] = $styleArray['fill']['startColor']['rgb'] = $RGB; - $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); - $styleArray['fill']['endColor']['rgb'] = $RGB2; - switch ($shade) { - case '1': - $styleArray['fill']['fillType'] = Fill::FILL_SOLID; - - break; - case '2': - $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_LINEAR; - - break; - case '3': - $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_PATH; - - break; - case '4': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKDOWN; - - break; - case '5': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRAY; - - break; - case '6': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRID; - - break; - case '7': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKHORIZONTAL; - - break; - case '8': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKTRELLIS; - - break; - case '9': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKUP; - - break; - case '10': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKVERTICAL; - - break; - case '11': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY0625; - - break; - case '12': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125; - - break; - case '13': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTDOWN; - - break; - case '14': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRAY; - - break; - case '15': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRID; - - break; - case '16': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTHORIZONTAL; - - break; - case '17': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTTRELLIS; - - break; - case '18': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTUP; - - break; - case '19': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTVERTICAL; - - break; - case '20': - $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_MEDIUMGRAY; - - break; - } - } - - $fontAttributes = $styleRegion->Style->Font->attributes(); - $styleArray['font']['name'] = (string) $styleRegion->Style->Font; - $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); - $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; - $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; - $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; - switch ($fontAttributes['Underline']) { - case '1': - $styleArray['font']['underline'] = Font::UNDERLINE_SINGLE; - - break; - case '2': - $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLE; - - break; - case '3': - $styleArray['font']['underline'] = Font::UNDERLINE_SINGLEACCOUNTING; - - break; - case '4': - $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLEACCOUNTING; - - break; - default: - $styleArray['font']['underline'] = Font::UNDERLINE_NONE; - - break; - } - switch ($fontAttributes['Script']) { - case '1': - $styleArray['font']['superscript'] = true; - - break; - case '-1': - $styleArray['font']['subscript'] = true; - - break; - } - - if (isset($styleRegion->Style->StyleBorder)) { - if (isset($styleRegion->Style->StyleBorder->Top)) { - $styleArray['borders']['top'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Top->attributes()); - } - if (isset($styleRegion->Style->StyleBorder->Bottom)) { - $styleArray['borders']['bottom'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Bottom->attributes()); - } - if (isset($styleRegion->Style->StyleBorder->Left)) { - $styleArray['borders']['left'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Left->attributes()); - } - if (isset($styleRegion->Style->StyleBorder->Right)) { - $styleArray['borders']['right'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Right->attributes()); - } - if ((isset($styleRegion->Style->StyleBorder->Diagonal)) && (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}))) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH; - } elseif (isset($styleRegion->Style->StyleBorder->Diagonal)) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP; - } elseif (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'})) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN; - } - } - if (isset($styleRegion->Style->HyperLink)) { - // TO DO - $hyperlink = $styleRegion->Style->HyperLink->attributes(); - } + break; } - $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); - } - } - } - if ((!$this->readDataOnly) && (isset($sheet->Cols))) { - // Column Widths - $columnAttributes = $sheet->Cols->attributes(); - $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4; - $c = 0; - foreach ($sheet->Cols->ColInfo as $columnOverride) { - $columnAttributes = $columnOverride->attributes(); - $column = $columnAttributes['No']; - $columnWidth = $columnAttributes['Unit'] / 5.4; - $hidden = (isset($columnAttributes['Hidden'])) && ($columnAttributes['Hidden'] == '1'); - $columnCount = (isset($columnAttributes['Count'])) ? $columnAttributes['Count'] : 1; - while ($c < $column) { - $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); - ++$c; - } - while (($c < ($column + $columnCount)) && ($c <= $maxCol)) { - $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($columnWidth); - if ($hidden) { - $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setVisible(false); + if (isset($styleRegion->Style->StyleBorder)) { + $srssb = $styleRegion->Style->StyleBorder; + $this->addBorderStyle($srssb, $styleArray, 'top'); + $this->addBorderStyle($srssb, $styleArray, 'bottom'); + $this->addBorderStyle($srssb, $styleArray, 'left'); + $this->addBorderStyle($srssb, $styleArray, 'right'); + $this->addBorderDiagonal($srssb, $styleArray); } - ++$c; - } - } - while ($c <= $maxCol) { - $spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); - ++$c; - } - } - - if ((!$this->readDataOnly) && (isset($sheet->Rows))) { - // Row Heights - $rowAttributes = $sheet->Rows->attributes(); - $defaultHeight = $rowAttributes['DefaultSizePts']; - $r = 0; - - foreach ($sheet->Rows->RowInfo as $rowOverride) { - $rowAttributes = $rowOverride->attributes(); - $row = $rowAttributes['No']; - $rowHeight = $rowAttributes['Unit']; - $hidden = (isset($rowAttributes['Hidden'])) && ($rowAttributes['Hidden'] == '1'); - $rowCount = (isset($rowAttributes['Count'])) ? $rowAttributes['Count'] : 1; - while ($r < $row) { - ++$r; - $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); - } - while (($r < ($row + $rowCount)) && ($r < $maxRow)) { - ++$r; - $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight); - if ($hidden) { - $spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false); + if (isset($styleRegion->Style->HyperLink)) { + // TO DO + $hyperlink = $styleRegion->Style->HyperLink->attributes(); } } - } - while ($r < $maxRow) { - ++$r; - $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); + $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); } } - // Handle Merged Cells in this worksheet - if (isset($sheet->MergedRegions)) { - foreach ($sheet->MergedRegions->Merge as $mergeCells) { - if (strpos($mergeCells, ':') !== false) { - $spreadsheet->getActiveSheet()->mergeCells($mergeCells); - } - } - } + $this->processColumnWidths($sheet, $maxCol); + $this->processRowHeights($sheet, $maxRow); + $this->processMergedCells($sheet); ++$worksheetID; } - // Loop through definedNames (global named ranges) - if (isset($gnmXML->Names)) { - foreach ($gnmXML->Names->Name as $namedRange) { - $name = (string) $namedRange->name; - $range = (string) $namedRange->value; - if (stripos($range, '#REF!') !== false) { - continue; - } + $this->processDefinedNames($gnmXML); - $range = Worksheet::extractSheetTitle($range, true); - $range[0] = trim($range[0], "'"); - if ($worksheet = $spreadsheet->getSheetByName($range[0])) { - $extractedRange = str_replace('$', '', $range[1]); - $spreadsheet->addNamedRange(new NamedRange($name, $worksheet, $extractedRange)); + // Return + return $this->spreadsheet; + } + + private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void + { + if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH; + } elseif (isset($srssb->Diagonal)) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP; + } elseif (isset($srssb->{'Rev-Diagonal'})) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN; + } + } + + private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void + { + $ucDirection = ucfirst($direction); + if (isset($srssb->$ucDirection)) { + $styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes()); + } + } + + private function processMergedCells(SimpleXMLElement $sheet): void + { + // Handle Merged Cells in this worksheet + if (isset($sheet->MergedRegions)) { + foreach ($sheet->MergedRegions->Merge as $mergeCells) { + if (strpos($mergeCells, ':') !== false) { + $this->spreadsheet->getActiveSheet()->mergeCells($mergeCells); } } } + } - // Return - return $spreadsheet; + private function processColumnLoop(int $c, int $maxCol, SimpleXMLElement $columnOverride, float $defaultWidth): int + { + $columnAttributes = $columnOverride->attributes(); + $column = $columnAttributes['No']; + $columnWidth = ((float) $columnAttributes['Unit']) / 5.4; + $hidden = (isset($columnAttributes['Hidden'])) && ((string) $columnAttributes['Hidden'] == '1'); + $columnCount = (isset($columnAttributes['Count'])) ? $columnAttributes['Count'] : 1; + while ($c < $column) { + $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); + ++$c; + } + while (($c < ($column + $columnCount)) && ($c <= $maxCol)) { + $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($columnWidth); + if ($hidden) { + $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setVisible(false); + } + ++$c; + } + + return $c; + } + + private function processColumnWidths(SimpleXMLElement $sheet, int $maxCol): void + { + if ((!$this->readDataOnly) && (isset($sheet->Cols))) { + // Column Widths + $columnAttributes = $sheet->Cols->attributes(); + $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4; + $c = 0; + foreach ($sheet->Cols->ColInfo as $columnOverride) { + $c = $this->processColumnLoop($c, $maxCol, $columnOverride, $defaultWidth); + } + while ($c <= $maxCol) { + $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); + ++$c; + } + } + } + + private function processRowLoop(int $r, int $maxRow, SimpleXMLElement $rowOverride, float $defaultHeight): int + { + $rowAttributes = $rowOverride->attributes(); + $row = $rowAttributes['No']; + $rowHeight = (float) $rowAttributes['Unit']; + $hidden = (isset($rowAttributes['Hidden'])) && ((string) $rowAttributes['Hidden'] == '1'); + $rowCount = (isset($rowAttributes['Count'])) ? $rowAttributes['Count'] : 1; + while ($r < $row) { + ++$r; + $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); + } + while (($r < ($row + $rowCount)) && ($r < $maxRow)) { + ++$r; + $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight); + if ($hidden) { + $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false); + } + } + + return $r; + } + + private function processRowHeights(SimpleXMLElement $sheet, int $maxRow): void + { + if ((!$this->readDataOnly) && (isset($sheet->Rows))) { + // Row Heights + $rowAttributes = $sheet->Rows->attributes(); + $defaultHeight = (float) $rowAttributes['DefaultSizePts']; + $r = 0; + + foreach ($sheet->Rows->RowInfo as $rowOverride) { + $r = $this->processRowLoop($r, $maxRow, $rowOverride, $defaultHeight); + } + // never executed, I can't figure out any circumstances + // under which it would be executed, and, even if + // such exist, I'm not convinced this is needed. + //while ($r < $maxRow) { + // ++$r; + // $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); + //} + } + } + + private function processDefinedNames(SimpleXMLElement $gnmXML): void + { + // Loop through definedNames (global named ranges) + if (isset($gnmXML->Names)) { + foreach ($gnmXML->Names->Name as $definedName) { + $name = (string) $definedName->name; + $value = (string) $definedName->value; + if (stripos($value, '#REF!') !== false) { + continue; + } + + [$worksheetName] = Worksheet::extractSheetTitle($value, true); + $worksheetName = trim($worksheetName, "'"); + $worksheet = $this->spreadsheet->getSheetByName($worksheetName); + // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet + if ($worksheet !== null) { + $this->spreadsheet->addDefinedName(DefinedName::createInstance($name, $worksheet, $value)); + } + } + } + } + + private function calcRotation(SimpleXMLElement $styleAttributes): int + { + $rotation = (int) $styleAttributes->Rotation; + if ($rotation >= 270 && $rotation <= 360) { + $rotation -= 360; + } + $rotation = (abs($rotation) > 90) ? 0 : $rotation; + + return $rotation; + } + + private static function addStyle(array &$styleArray, string $key, string $value): void + { + if (array_key_exists($value, self::$mappings[$key])) { + $styleArray[$key] = self::$mappings[$key][$value]; + } + } + + private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void + { + if (array_key_exists($value, self::$mappings[$key])) { + $styleArray[$key1][$key] = self::$mappings[$key][$value]; + } } private static function parseBorderAttributes($borderAttributes) @@ -807,64 +787,7 @@ class Gnumeric extends BaseReader $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); } - switch ($borderAttributes['Style']) { - case '0': - $styleArray['borderStyle'] = Border::BORDER_NONE; - - break; - case '1': - $styleArray['borderStyle'] = Border::BORDER_THIN; - - break; - case '2': - $styleArray['borderStyle'] = Border::BORDER_MEDIUM; - - break; - case '3': - $styleArray['borderStyle'] = Border::BORDER_SLANTDASHDOT; - - break; - case '4': - $styleArray['borderStyle'] = Border::BORDER_DASHED; - - break; - case '5': - $styleArray['borderStyle'] = Border::BORDER_THICK; - - break; - case '6': - $styleArray['borderStyle'] = Border::BORDER_DOUBLE; - - break; - case '7': - $styleArray['borderStyle'] = Border::BORDER_DOTTED; - - break; - case '8': - $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHED; - - break; - case '9': - $styleArray['borderStyle'] = Border::BORDER_DASHDOT; - - break; - case '10': - $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOT; - - break; - case '11': - $styleArray['borderStyle'] = Border::BORDER_DASHDOTDOT; - - break; - case '12': - $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT; - - break; - case '13': - $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT; - - break; - } + self::addStyle($styleArray, 'borderStyle', $borderAttributes['Style']); return $styleArray; } @@ -886,4 +809,23 @@ class Gnumeric extends BaseReader return $gnmR . $gnmG . $gnmB; } + + private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void + { + $RGB = self::parseGnumericColour($styleAttributes['Fore']); + $styleArray['font']['color']['rgb'] = $RGB; + $RGB = self::parseGnumericColour($styleAttributes['Back']); + $shade = (string) $styleAttributes['Shade']; + if (($RGB != '000000') || ($shade != '0')) { + $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); + if ($shade == '1') { + $styleArray['fill']['startColor']['rgb'] = $RGB; + $styleArray['fill']['endColor']['rgb'] = $RGB2; + } else { + $styleArray['fill']['endColor']['rgb'] = $RGB; + $styleArray['fill']['startColor']['rgb'] = $RGB2; + } + self::addStyle2($styleArray, 'fill', 'fillType', $shade); + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php new file mode 100644 index 00000000000..0fe73005939 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php @@ -0,0 +1,143 @@ +spreadsheet = $spreadsheet; + $this->gnm = $gnm; + } + + public function printInformation(SimpleXMLElement $sheet): self + { + if (isset($sheet->PrintInformation)) { + $printInformation = $sheet->PrintInformation[0]; + $scale = (string) $printInformation->Scale->attributes()['percentage']; + $pageOrder = (string) $printInformation->order; + $orientation = (string) $printInformation->orientation; + $horizontalCentered = (string) $printInformation->hcenter->attributes()['value']; + $verticalCentered = (string) $printInformation->vcenter->attributes()['value']; + + $this->spreadsheet->getActiveSheet()->getPageSetup() + ->setPageOrder($pageOrder === 'r_then_d' ? WorksheetPageSetup::PAGEORDER_OVER_THEN_DOWN : WorksheetPageSetup::PAGEORDER_DOWN_THEN_OVER) + ->setScale((int) $scale) + ->setOrientation($orientation ?? WorksheetPageSetup::ORIENTATION_DEFAULT) + ->setHorizontalCentered((bool) $horizontalCentered) + ->setVerticalCentered((bool) $verticalCentered); + } + + return $this; + } + + public function sheetMargins(SimpleXMLElement $sheet): self + { + if (isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) { + $marginSet = [ + // Default Settings + 'top' => 0.75, + 'header' => 0.3, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.75, + 'footer' => 0.3, + ]; + + $marginSet = $this->buildMarginSet($sheet, $marginSet); + $this->adjustMargins($marginSet); + } + + return $this; + } + + private function buildMarginSet(SimpleXMLElement $sheet, array $marginSet): array + { + foreach ($sheet->PrintInformation->Margins->children($this->gnm, true) as $key => $margin) { + $marginAttributes = $margin->attributes(); + $marginSize = ($marginAttributes['Points']) ?? 72; // Default is 72pt + // Convert value in points to inches + $marginSize = PageMargins::fromPoints((float) $marginSize); + $marginSet[$key] = $marginSize; + } + + return $marginSet; + } + + private function adjustMargins(array $marginSet): void + { + foreach ($marginSet as $key => $marginSize) { + // Gnumeric is quirky in the way it displays the header/footer values: + // header is actually the sum of top and header; footer is actually the sum of bottom and footer + // then top is actually the header value, and bottom is actually the footer value + switch ($key) { + case 'left': + case 'right': + $this->sheetMargin($key, $marginSize); + + break; + case 'top': + $this->sheetMargin($key, $marginSet['header'] ?? 0); + + break; + case 'bottom': + $this->sheetMargin($key, $marginSet['footer'] ?? 0); + + break; + case 'header': + $this->sheetMargin($key, ($marginSet['top'] ?? 0) - $marginSize); + + break; + case 'footer': + $this->sheetMargin($key, ($marginSet['bottom'] ?? 0) - $marginSize); + + break; + } + } + } + + private function sheetMargin(string $key, float $marginSize): void + { + switch ($key) { + case 'top': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize); + + break; + case 'bottom': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize); + + break; + case 'left': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize); + + break; + case 'right': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize); + + break; + case 'header': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize); + + break; + case 'footer': + $this->spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize); + + break; + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php index b500d3ae3bc..73f4591e7f1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php @@ -16,6 +16,7 @@ use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use Throwable; /** PhpSpreadsheet root directory */ class Html extends BaseReader @@ -205,8 +206,6 @@ class Html extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -221,9 +220,13 @@ class Html extends BaseReader /** * Set input encoding. * + * @deprecated no use is made of this property + * * @param string $pValue Input encoding, eg: 'ANSI' * - * @return Html + * @return $this + * + * @codeCoverageIgnore */ public function setInputEncoding($pValue) { @@ -235,7 +238,11 @@ class Html extends BaseReader /** * Get input encoding. * + * @deprecated no use is made of this property + * * @return string + * + * @codeCoverageIgnore */ public function getInputEncoding() { @@ -272,7 +279,7 @@ class Html extends BaseReader return array_pop($this->nestedColumn); } - protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent) + protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void { if (is_string($cellContent)) { // Simple String content @@ -291,14 +298,319 @@ class Html extends BaseReader $cellContent = (string) ''; } - /** - * @param DOMNode $element - * @param Worksheet $sheet - * @param int $row - * @param string $column - * @param string $cellContent - */ - protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, &$column, &$cellContent) + private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void + { + $attributeArray = []; + foreach ($child->attributes as $attribute) { + $attributeArray[$attribute->name] = $attribute->value; + } + + if ($child->nodeName === 'body') { + $row = 1; + $column = 'A'; + $cellContent = ''; + $this->tableLevel = 0; + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + } else { + $this->processDomElementTitle($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementTitle(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'title') { + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + $sheet->setTitle($cellContent, true, false); + $cellContent = ''; + } else { + $this->processDomElementSpanEtc($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private static $spanEtc = ['span', 'div', 'font', 'i', 'em', 'strong', 'b']; + + private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if (in_array($child->nodeName, self::$spanEtc)) { + if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') { + $sheet->getComment($column . $row) + ->getText() + ->createTextRun($child->textContent); + } + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + + if (isset($this->formats[$child->nodeName])) { + $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); + } + } else { + $this->processDomElementHr($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'hr') { + $this->flushCell($sheet, $column, $row, $cellContent); + ++$row; + if (isset($this->formats[$child->nodeName])) { + $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); + } + ++$row; + } + // fall through to br + $this->processDomElementBr($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + + private function processDomElementBr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'br' || $child->nodeName === 'hr') { + if ($this->tableLevel > 0) { + // If we're inside a table, replace with a \n and set the cell to wrap + $cellContent .= "\n"; + $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true); + } else { + // Otherwise flush our existing content and move the row cursor on + $this->flushCell($sheet, $column, $row, $cellContent); + ++$row; + } + } else { + $this->processDomElementA($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementA(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'a') { + foreach ($attributeArray as $attributeName => $attributeValue) { + switch ($attributeName) { + case 'href': + $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue); + if (isset($this->formats[$child->nodeName])) { + $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); + } + + break; + case 'class': + if ($attributeValue === 'comment-indicator') { + break; // Ignore - it's just a red square. + } + } + } + // no idea why this should be needed + //$cellContent .= ' '; + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + } else { + $this->processDomElementH1Etc($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private static $h1Etc = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p']; + + private function processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if (in_array($child->nodeName, self::$h1Etc)) { + if ($this->tableLevel > 0) { + // If we're inside a table, replace with a \n + $cellContent .= $cellContent ? "\n" : ''; + $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true); + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + } else { + if ($cellContent > '') { + $this->flushCell($sheet, $column, $row, $cellContent); + ++$row; + } + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + $this->flushCell($sheet, $column, $row, $cellContent); + + if (isset($this->formats[$child->nodeName])) { + $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); + } + + ++$row; + $column = 'A'; + } + } else { + $this->processDomElementLi($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementLi(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'li') { + if ($this->tableLevel > 0) { + // If we're inside a table, replace with a \n + $cellContent .= $cellContent ? "\n" : ''; + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + } else { + if ($cellContent > '') { + $this->flushCell($sheet, $column, $row, $cellContent); + } + ++$row; + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + $this->flushCell($sheet, $column, $row, $cellContent); + $column = 'A'; + } + } else { + $this->processDomElementImg($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementImg(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'img') { + $this->insertImage($sheet, $column, $row, $attributeArray); + } else { + $this->processDomElementTable($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'table') { + $this->flushCell($sheet, $column, $row, $cellContent); + $column = $this->setTableStartColumn($column); + if ($this->tableLevel > 1) { + --$row; + } + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + $column = $this->releaseTableStartColumn(); + if ($this->tableLevel > 1) { + ++$column; + } else { + ++$row; + } + } else { + $this->processDomElementTr($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName === 'tr') { + $column = $this->getTableStartColumn(); + $cellContent = ''; + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + + if (isset($attributeArray['height'])) { + $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']); + } + + ++$row; + } else { + $this->processDomElementThTdOther($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementThTdOther(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + if ($child->nodeName !== 'td' && $child->nodeName !== 'th') { + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + } else { + $this->processDomElementThTd($sheet, $row, $column, $cellContent, $child, $attributeArray); + } + } + + private function processDomElementBgcolor(Worksheet $sheet, int $row, string $column, array $attributeArray): void + { + if (isset($attributeArray['bgcolor'])) { + $sheet->getStyle("$column$row")->applyFromArray( + [ + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'color' => ['rgb' => $this->getStyleColor($attributeArray['bgcolor'])], + ], + ] + ); + } + } + + private function processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray): void + { + if (isset($attributeArray['width'])) { + $sheet->getColumnDimension($column)->setWidth($attributeArray['width']); + } + } + + private function processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray): void + { + if (isset($attributeArray['height'])) { + $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']); + } + } + + private function processDomElementAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void + { + if (isset($attributeArray['align'])) { + $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']); + } + } + + private function processDomElementVAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void + { + if (isset($attributeArray['valign'])) { + $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']); + } + } + + private function processDomElementDataFormat(Worksheet $sheet, int $row, string $column, array $attributeArray): void + { + if (isset($attributeArray['data-format'])) { + $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']); + } + } + + private function processDomElementThTd(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void + { + while (isset($this->rowspan[$column . $row])) { + ++$column; + } + $this->processDomElement($child, $sheet, $row, $column, $cellContent); + + // apply inline style + $this->applyInlineStyle($sheet, $row, $column, $attributeArray); + + $this->flushCell($sheet, $column, $row, $cellContent); + + $this->processDomElementBgcolor($sheet, $row, $column, $attributeArray); + $this->processDomElementWidth($sheet, $column, $attributeArray); + $this->processDomElementHeight($sheet, $row, $attributeArray); + $this->processDomElementAlign($sheet, $row, $column, $attributeArray); + $this->processDomElementVAlign($sheet, $row, $column, $attributeArray); + $this->processDomElementDataFormat($sheet, $row, $column, $attributeArray); + + if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) { + //create merging rowspan and colspan + $columnTo = $column; + for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { + ++$columnTo; + } + $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1); + foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) { + $this->rowspan[$value] = true; + } + $sheet->mergeCells($range); + $column = $columnTo; + } elseif (isset($attributeArray['rowspan'])) { + //create merging rowspan + $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1); + foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) { + $this->rowspan[$value] = true; + } + $sheet->mergeCells($range); + } elseif (isset($attributeArray['colspan'])) { + //create merging colspan + $columnTo = $column; + for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { + ++$columnTo; + } + $sheet->mergeCells($column . $row . ':' . $columnTo . $row); + $column = $columnTo; + } + + ++$column; + } + + protected function processDomElement(DOMNode $element, Worksheet $sheet, int &$row, string &$column, string &$cellContent): void { foreach ($element->childNodes as $child) { if ($child instanceof DOMText) { @@ -310,267 +622,7 @@ class Html extends BaseReader // but if we have a rich text run instead, we need to append it correctly // TODO } elseif ($child instanceof DOMElement) { - $attributeArray = []; - foreach ($child->attributes as $attribute) { - $attributeArray[$attribute->name] = $attribute->value; - } - - switch ($child->nodeName) { - case 'meta': - foreach ($attributeArray as $attributeName => $attributeValue) { - // Extract character set, so we can convert to UTF-8 if required - if ($attributeName === 'charset') { - $this->setInputEncoding($attributeValue); - } - } - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - break; - case 'title': - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - $sheet->setTitle($cellContent, true, false); - $cellContent = ''; - - break; - case 'span': - case 'div': - case 'font': - case 'i': - case 'em': - case 'strong': - case 'b': - if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') { - $sheet->getComment($column . $row) - ->getText() - ->createTextRun($child->textContent); - - break; - } - - if ($cellContent > '') { - $cellContent .= ' '; - } - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - if ($cellContent > '') { - $cellContent .= ' '; - } - - if (isset($this->formats[$child->nodeName])) { - $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); - } - - break; - case 'hr': - $this->flushCell($sheet, $column, $row, $cellContent); - ++$row; - if (isset($this->formats[$child->nodeName])) { - $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); - } else { - $cellContent = '----------'; - $this->flushCell($sheet, $column, $row, $cellContent); - } - ++$row; - // Add a break after a horizontal rule, simply by allowing the code to dropthru - // no break - case 'br': - if ($this->tableLevel > 0) { - // If we're inside a table, replace with a \n and set the cell to wrap - $cellContent .= "\n"; - $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true); - } else { - // Otherwise flush our existing content and move the row cursor on - $this->flushCell($sheet, $column, $row, $cellContent); - ++$row; - } - - break; - case 'a': - foreach ($attributeArray as $attributeName => $attributeValue) { - switch ($attributeName) { - case 'href': - $sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue); - if (isset($this->formats[$child->nodeName])) { - $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); - } - - break; - case 'class': - if ($attributeValue === 'comment-indicator') { - break; // Ignore - it's just a red square. - } - } - } - $cellContent .= ' '; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - break; - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - case 'ol': - case 'ul': - case 'p': - if ($this->tableLevel > 0) { - // If we're inside a table, replace with a \n - $cellContent .= "\n"; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - } else { - if ($cellContent > '') { - $this->flushCell($sheet, $column, $row, $cellContent); - ++$row; - } - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - $this->flushCell($sheet, $column, $row, $cellContent); - - if (isset($this->formats[$child->nodeName])) { - $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]); - } - - ++$row; - $column = 'A'; - } - - break; - case 'li': - if ($this->tableLevel > 0) { - // If we're inside a table, replace with a \n - $cellContent .= "\n"; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - } else { - if ($cellContent > '') { - $this->flushCell($sheet, $column, $row, $cellContent); - } - ++$row; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - $this->flushCell($sheet, $column, $row, $cellContent); - $column = 'A'; - } - - break; - case 'img': - $this->insertImage($sheet, $column, $row, $attributeArray); - - break; - case 'table': - $this->flushCell($sheet, $column, $row, $cellContent); - $column = $this->setTableStartColumn($column); - if ($this->tableLevel > 1) { - --$row; - } - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - $column = $this->releaseTableStartColumn(); - if ($this->tableLevel > 1) { - ++$column; - } else { - ++$row; - } - - break; - case 'thead': - case 'tbody': - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - break; - case 'tr': - $column = $this->getTableStartColumn(); - $cellContent = ''; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - if (isset($attributeArray['height'])) { - $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']); - } - - ++$row; - - break; - case 'th': - case 'td': - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - while (isset($this->rowspan[$column . $row])) { - ++$column; - } - - // apply inline style - $this->applyInlineStyle($sheet, $row, $column, $attributeArray); - - $this->flushCell($sheet, $column, $row, $cellContent); - - if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) { - //create merging rowspan and colspan - $columnTo = $column; - for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { - ++$columnTo; - } - $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1); - foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) { - $this->rowspan[$value] = true; - } - $sheet->mergeCells($range); - $column = $columnTo; - } elseif (isset($attributeArray['rowspan'])) { - //create merging rowspan - $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1); - foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) { - $this->rowspan[$value] = true; - } - $sheet->mergeCells($range); - } elseif (isset($attributeArray['colspan'])) { - //create merging colspan - $columnTo = $column; - for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { - ++$columnTo; - } - $sheet->mergeCells($column . $row . ':' . $columnTo . $row); - $column = $columnTo; - } elseif (isset($attributeArray['bgcolor'])) { - $sheet->getStyle($column . $row)->applyFromArray( - [ - 'fill' => [ - 'fillType' => Fill::FILL_SOLID, - 'color' => ['rgb' => $attributeArray['bgcolor']], - ], - ] - ); - } - - if (isset($attributeArray['width'])) { - $sheet->getColumnDimension($column)->setWidth($attributeArray['width']); - } - - if (isset($attributeArray['height'])) { - $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']); - } - - if (isset($attributeArray['align'])) { - $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']); - } - - if (isset($attributeArray['valign'])) { - $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']); - } - - if (isset($attributeArray['data-format'])) { - $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']); - } - - ++$column; - - break; - case 'body': - $row = 1; - $column = 'A'; - $cellContent = ''; - $this->tableLevel = 0; - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - - break; - default: - $this->processDomElement($child, $sheet, $row, $column, $cellContent); - } + $this->processDomElementBody($sheet, $row, $column, $cellContent, $child); } } } @@ -579,9 +631,6 @@ class Html extends BaseReader * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. * * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception * * @return Spreadsheet */ @@ -595,7 +644,11 @@ class Html extends BaseReader // Create a new DOM object $dom = new DOMDocument(); // Reload the HTML file into the DOM object - $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8')); + try { + $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8')); + } catch (Throwable $e) { + $loaded = false; + } if ($loaded === false) { throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document'); } @@ -607,16 +660,17 @@ class Html extends BaseReader * Spreadsheet from content. * * @param string $content - * @param null|Spreadsheet $spreadsheet - * - * @return Spreadsheet */ public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet { // Create a new DOM object $dom = new DOMDocument(); // Reload the HTML file into the DOM object - $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8')); + try { + $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8')); + } catch (Throwable $e) { + $loaded = false; + } if ($loaded === false) { throw new Exception('Failed to load content as a DOM Document'); } @@ -626,13 +680,6 @@ class Html extends BaseReader /** * Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance. - * - * @param DOMDocument $document - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Exception - * - * @return Spreadsheet */ private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet { @@ -669,7 +716,7 @@ class Html extends BaseReader * * @param int $pValue Sheet index * - * @return HTML + * @return $this */ public function setSheetIndex($pValue) { @@ -693,13 +740,32 @@ class Html extends BaseReader * @param string $column * @param array $attributeArray */ - private function applyInlineStyle(&$sheet, $row, $column, $attributeArray) + private function applyInlineStyle(&$sheet, $row, $column, $attributeArray): void { if (!isset($attributeArray['style'])) { return; } - $cellStyle = $sheet->getStyle($column . $row); + if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) { + $columnTo = $column; + for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { + ++$columnTo; + } + $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1); + $cellStyle = $sheet->getStyle($range); + } elseif (isset($attributeArray['rowspan'])) { + $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1); + $cellStyle = $sheet->getStyle($range); + } elseif (isset($attributeArray['colspan'])) { + $columnTo = $column; + for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) { + ++$columnTo; + } + $range = $column . $row . ':' . $columnTo . $row; + $cellStyle = $sheet->getStyle($range); + } else { + $cellStyle = $sheet->getStyle($column . $row); + } // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color $styles = explode(';', $attributeArray['style']); @@ -854,18 +920,14 @@ class Html extends BaseReader return substr($value, 1); } - return null; + return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup((string) $value); } /** - * @param Worksheet $sheet * @param string $column * @param int $row - * @param array $attributes - * - * @throws \PhpOffice\PhpSpreadsheet\Exception */ - private function insertImage(Worksheet $sheet, $column, $row, array $attributes) + private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void { if (!isset($attributes['src'])) { return; @@ -874,7 +936,7 @@ class Html extends BaseReader $src = urldecode($attributes['src']); $width = isset($attributes['width']) ? (float) $attributes['width'] : null; $height = isset($attributes['height']) ? (float) $attributes['height'] : null; - $name = isset($attributes['alt']) ? (float) $attributes['alt'] : null; + $name = $attributes['alt'] ?? null; $drawing = new Drawing(); $drawing->setPath($src); @@ -905,6 +967,28 @@ class Html extends BaseReader ); } + private static $borderMappings = [ + 'dash-dot' => Border::BORDER_DASHDOT, + 'dash-dot-dot' => Border::BORDER_DASHDOTDOT, + 'dashed' => Border::BORDER_DASHED, + 'dotted' => Border::BORDER_DOTTED, + 'double' => Border::BORDER_DOUBLE, + 'hair' => Border::BORDER_HAIR, + 'medium' => Border::BORDER_MEDIUM, + 'medium-dashed' => Border::BORDER_MEDIUMDASHED, + 'medium-dash-dot' => Border::BORDER_MEDIUMDASHDOT, + 'medium-dash-dot-dot' => Border::BORDER_MEDIUMDASHDOTDOT, + 'none' => Border::BORDER_NONE, + 'slant-dash-dot' => Border::BORDER_SLANTDASHDOT, + 'solid' => Border::BORDER_THIN, + 'thick' => Border::BORDER_THICK, + ]; + + public static function getBorderMappings(): array + { + return self::$borderMappings; + } + /** * Map html border style to PhpSpreadsheet border style. * @@ -914,48 +998,21 @@ class Html extends BaseReader */ public function getBorderStyle($style) { - switch ($style) { - case 'solid': - return Border::BORDER_THIN; - case 'dashed': - return Border::BORDER_DASHED; - case 'dotted': - return Border::BORDER_DOTTED; - case 'medium': - return Border::BORDER_MEDIUM; - case 'thick': - return Border::BORDER_THICK; - case 'none': - return Border::BORDER_NONE; - case 'dash-dot': - return Border::BORDER_DASHDOT; - case 'dash-dot-dot': - return Border::BORDER_DASHDOTDOT; - case 'double': - return Border::BORDER_DOUBLE; - case 'hair': - return Border::BORDER_HAIR; - case 'medium-dash-dot': - return Border::BORDER_MEDIUMDASHDOT; - case 'medium-dash-dot-dot': - return Border::BORDER_MEDIUMDASHDOTDOT; - case 'medium-dashed': - return Border::BORDER_MEDIUMDASHED; - case 'slant-dash-dot': - return Border::BORDER_SLANTDASHDOT; - } - - return null; + return (array_key_exists($style, self::$borderMappings)) ? self::$borderMappings[$style] : null; } /** - * @param Style $cellStyle * @param string $styleValue * @param string $type */ - private function setBorderStyle(Style $cellStyle, $styleValue, $type) + private function setBorderStyle(Style $cellStyle, $styleValue, $type): void { - [, $borderStyle, $color] = explode(' ', $styleValue); + if (trim($styleValue) === Border::BORDER_NONE) { + $borderStyle = Border::BORDER_NONE; + $color = null; + } else { + [, $borderStyle, $color] = explode(' ', $styleValue); + } $cellStyle->applyFromArray([ 'borders' => [ diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php index 70a7a200d78..a8bd3606596 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php @@ -118,8 +118,6 @@ interface IReader /** * Set read filter. * - * @param IReadFilter $pValue - * * @return IReader */ public function setReadFilter(IReadFilter $pValue); @@ -129,8 +127,6 @@ interface IReader * * @param string $pFilename * - * @throws Exception - * * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ public function load($pFilename); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php index 5fff07aa383..4ceac65301d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php @@ -4,9 +4,16 @@ namespace PhpOffice\PhpSpreadsheet\Reader; use DateTime; use DateTimeZone; +use DOMAttr; +use DOMDocument; +use DOMElement; +use DOMNode; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\DefinedName; +use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; +use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings; use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\RichText\RichText; @@ -15,6 +22,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use XMLReader; use ZipArchive; @@ -34,8 +42,6 @@ class Ods extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return bool */ public function canRead($pFilename) @@ -73,11 +79,9 @@ class Ods extends BaseReader } $zip->close(); - - return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet'; } - return false; + return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet'; } /** @@ -85,8 +89,6 @@ class Ods extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return string[] */ public function listWorksheetNames($pFilename) @@ -94,8 +96,8 @@ class Ods extends BaseReader File::assertFile($pFilename); $zip = new ZipArchive(); - if (!$zip->open($pFilename)) { - throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); + if ($zip->open($pFilename) !== true) { + throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.'); } $worksheetNames = []; @@ -139,8 +141,6 @@ class Ods extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetInfo($pFilename) @@ -150,8 +150,8 @@ class Ods extends BaseReader $worksheetInfo = []; $zip = new ZipArchive(); - if (!$zip->open($pFilename)) { - throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.'); + if ($zip->open($pFilename) !== true) { + throw new ReaderException('Could not open ' . $pFilename . ' for reading! Error opening file.'); } $xml = new XMLReader(); @@ -199,18 +199,18 @@ class Ods extends BaseReader // Step into the row $xml->read(); do { + $doread = true; if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { if (!$xml->isEmptyElement) { ++$currCells; $xml->next(); - } else { - $xml->read(); + $doread = false; } } elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) { $mergeSize = $xml->getAttribute('table:number-columns-repeated'); $currCells += (int) $mergeSize; - $xml->read(); - } else { + } + if ($doread) { $xml->read(); } } while ($xml->name != 'table:table-row'); @@ -233,8 +233,6 @@ class Ods extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -250,9 +248,6 @@ class Ods extends BaseReader * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. * * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception * * @return Spreadsheet */ @@ -261,16 +256,16 @@ class Ods extends BaseReader File::assertFile($pFilename); $timezoneObj = new DateTimeZone('Europe/London'); - $GMT = new \DateTimeZone('UTC'); + $GMT = new DateTimeZone('UTC'); $zip = new ZipArchive(); - if (!$zip->open($pFilename)) { + if ($zip->open($pFilename) !== true) { throw new Exception("Could not open {$pFilename} for reading! Error opening file."); } // Meta - $xml = simplexml_load_string( + $xml = @simplexml_load_string( $this->securityScanner->scan($zip->getFromName('meta.xml')), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions() @@ -283,9 +278,19 @@ class Ods extends BaseReader (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta); - // Content + // Styles - $dom = new \DOMDocument('1.01', 'UTF-8'); + $dom = new DOMDocument('1.01', 'UTF-8'); + $dom->loadXML( + $this->securityScanner->scan($zip->getFromName('styles.xml')), + Settings::getLibXmlLoaderOptions() + ); + + $pageSettings = new PageSettings($dom); + + // Main Content + + $dom = new DOMDocument('1.01', 'UTF-8'); $dom->loadXML( $this->securityScanner->scan($zip->getFromName('content.xml')), Settings::getLibXmlLoaderOptions() @@ -296,26 +301,34 @@ class Ods extends BaseReader $textNs = $dom->lookupNamespaceUri('text'); $xlinkNs = $dom->lookupNamespaceUri('xlink'); + $pageSettings->readStyleCrossReferences($dom); + + // Content + $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body') ->item(0) ->getElementsByTagNameNS($officeNs, 'spreadsheet'); foreach ($spreadsheets as $workbookData) { - /** @var \DOMElement $workbookData */ + /** @var DOMElement $workbookData */ $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table'); $worksheetID = 0; foreach ($tables as $worksheetDataSet) { - /** @var \DOMElement $worksheetDataSet */ + /** @var DOMElement $worksheetDataSet */ $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name'); // Check loadSheetsOnly - if (isset($this->loadSheetsOnly) + if ( + isset($this->loadSheetsOnly) && $worksheetName - && !in_array($worksheetName, $this->loadSheetsOnly)) { + && !in_array($worksheetName, $this->loadSheetsOnly) + ) { continue; } + $worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name'); + // Create sheet if ($worksheetID > 0) { $spreadsheet->createSheet(); // First sheet is added by default @@ -326,13 +339,13 @@ class Ods extends BaseReader // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in // formula cells... during the load, all formulae should be correct, and we're simply // bringing the worksheet name in line with the formula, not the reverse - $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); + $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false); } // Go through every child of table element $rowID = 1; foreach ($worksheetDataSet->childNodes as $childNode) { - /** @var \DOMElement $childNode */ + /** @var DOMElement $childNode */ // Filter elements which are not under the "table" ns if ($childNode->namespaceURI != $tableNs) { @@ -405,11 +418,11 @@ class Ods extends BaseReader // Content - /** @var \DOMElement[] $paragraphs */ + /** @var DOMElement[] $paragraphs */ $paragraphs = []; foreach ($cellData->childNodes as $item) { - /** @var \DOMElement $item */ + /** @var DOMElement $item */ // Filter text:p elements if ($item->nodeName == 'text:p') { @@ -455,9 +468,10 @@ class Ods extends BaseReader $type = DataType::TYPE_NUMERIC; $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value'); - if (floor($dataValue) == $dataValue) { - $dataValue = (int) $dataValue; - } + // percentage should always be float + //if (floor($dataValue) == $dataValue) { + // $dataValue = (int) $dataValue; + //} $formatting = NumberFormat::FORMAT_PERCENTAGE_00; break; @@ -478,8 +492,6 @@ class Ods extends BaseReader if (floor($dataValue) == $dataValue) { if ($dataValue == (int) $dataValue) { $dataValue = (int) $dataValue; - } else { - $dataValue = (float) $dataValue; } } @@ -537,30 +549,7 @@ class Ods extends BaseReader if ($hasCalculatedValue) { $type = DataType::TYPE_FORMULA; $cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1); - $temp = explode('"', $cellDataFormula); - $tKey = false; - foreach ($temp as &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) - if ($tKey = !$tKey) { - // Cell range reference in another sheet - $value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/U', '$1!$2:$3', $value); - - // Cell reference in another sheet - $value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/U', '$1!$2', $value); - - // Cell range reference - $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/U', '$1:$2', $value); - - // Simple cell reference - $value = preg_replace('/\[\.([^\.]+)\]/U', '$1', $value); - - $value = Calculation::translateSeparator(';', ',', $value, $inBraces); - } - } - unset($value); - - // Then rebuild the formula string - $cellDataFormula = implode('"', $temp); + $cellDataFormula = $this->convertToExcelFormulaValue($cellDataFormula); } if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) { @@ -616,7 +605,8 @@ class Ods extends BaseReader } // Merged cells - if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned') + if ( + $cellData->hasAttributeNS($tableNs, 'number-columns-spanned') || $cellData->hasAttributeNS($tableNs, 'number-rows-spanned') ) { if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) { @@ -648,10 +638,14 @@ class Ods extends BaseReader break; } } + $pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName); ++$worksheetID; } - } + $this->readDefinedRanges($spreadsheet, $workbookData, $tableNs); + $this->readDefinedExpressions($spreadsheet, $workbookData, $tableNs); + } + $spreadsheet->setActiveSheetIndex(0); // Return return $spreadsheet; } @@ -659,22 +653,20 @@ class Ods extends BaseReader /** * Recursively scan element. * - * @param \DOMNode $element - * * @return string */ - protected function scanElementForText(\DOMNode $element) + protected function scanElementForText(DOMNode $element) { $str = ''; foreach ($element->childNodes as $child) { - /** @var \DOMNode $child */ + /** @var DOMNode $child */ if ($child->nodeType == XML_TEXT_NODE) { $str .= $child->nodeValue; } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') { // It's a space // Multiple spaces? - /** @var \DOMAttr $cAttr */ + /** @var DOMAttr $cAttr */ $cAttr = $child->attributes->getNamedItem('c'); if ($cAttr) { $multiplier = (int) $cAttr->nodeValue; @@ -705,4 +697,99 @@ class Ods extends BaseReader return $value; } + + private function convertToExcelAddressValue(string $openOfficeAddress): string + { + $excelAddress = $openOfficeAddress; + + // Cell range 3-d reference + // As we don't support 3-d ranges, we're just going to take a quick and dirty approach + // and assume that the second worksheet reference is the same as the first + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress); + // Cell range reference in another sheet + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress); + // Cell reference in another sheet + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress); + // Cell range reference + $excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress); + // Simple cell reference + $excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress); + + return $excelAddress; + } + + private function convertToExcelFormulaValue(string $openOfficeFormula): string + { + $temp = explode('"', $openOfficeFormula); + $tKey = false; + foreach ($temp as &$value) { + // Only replace in alternate array entries (i.e. non-quoted blocks) + if ($tKey = !$tKey) { + // Cell range reference in another sheet + $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value); + // Cell reference in another sheet + $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value); + // Cell range reference + $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value); + // Simple cell reference + $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value); + + $value = Calculation::translateSeparator(';', ',', $value, $inBraces); + } + } + + // Then rebuild the formula string + $excelFormula = implode('"', $temp); + + return $excelFormula; + } + + /** + * Read any Named Ranges that are defined in this spreadsheet. + */ + private function readDefinedRanges(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void + { + $namedRanges = $workbookData->getElementsByTagNameNS($tableNs, 'named-range'); + foreach ($namedRanges as $definedNameElement) { + $definedName = $definedNameElement->getAttributeNS($tableNs, 'name'); + $baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address'); + $range = $definedNameElement->getAttributeNS($tableNs, 'cell-range-address'); + + $baseAddress = $this->convertToExcelAddressValue($baseAddress); + $range = $this->convertToExcelAddressValue($range); + + $this->addDefinedName($spreadsheet, $baseAddress, $definedName, $range); + } + } + + /** + * Read any Named Formulae that are defined in this spreadsheet. + */ + private function readDefinedExpressions(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void + { + $namedExpressions = $workbookData->getElementsByTagNameNS($tableNs, 'named-expression'); + foreach ($namedExpressions as $definedNameElement) { + $definedName = $definedNameElement->getAttributeNS($tableNs, 'name'); + $baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address'); + $expression = $definedNameElement->getAttributeNS($tableNs, 'expression'); + + $baseAddress = $this->convertToExcelAddressValue($baseAddress); + $expression = $this->convertToExcelFormulaValue($expression); + + $this->addDefinedName($spreadsheet, $baseAddress, $definedName, $expression); + } + } + + /** + * Assess scope and store the Defined Name. + */ + private function addDefinedName(Spreadsheet $spreadsheet, string $baseAddress, string $definedName, string $value): void + { + [$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true); + $worksheet = $spreadsheet->getSheetByName($sheetReference); + // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet + if ($worksheet !== null) { + $spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value)); + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php new file mode 100644 index 00000000000..77341aab856 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -0,0 +1,139 @@ +setDomNameSpaces($styleDom); + $this->readPageSettingStyles($styleDom); + $this->readStyleMasterLookup($styleDom); + } + + private function setDomNameSpaces(DOMDocument $styleDom): void + { + $this->officeNs = $styleDom->lookupNamespaceUri('office'); + $this->stylesNs = $styleDom->lookupNamespaceUri('style'); + $this->stylesFo = $styleDom->lookupNamespaceUri('fo'); + } + + private function readPageSettingStyles(DOMDocument $styleDom): void + { + $styles = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'page-layout'); + + foreach ($styles as $styleSet) { + $styleName = $styleSet->getAttributeNS($this->stylesNs, 'name'); + $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')[0]; + $styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation'); + $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to'); + $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order'); + $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering'); + + $marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left'); + $marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right'); + $marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top'); + $marginBottom = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-bottom'); + $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')[0]; + $headerProperties = $header->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0]; + $marginHeader = $headerProperties->getAttributeNS($this->stylesFo, 'min-height'); + $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')[0]; + $footerProperties = $footer->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0]; + $marginFooter = $footerProperties->getAttributeNS($this->stylesFo, 'min-height'); + + $this->pageLayoutStyles[$styleName] = (object) [ + 'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT, + 'scale' => $styleScale ?: 100, + 'printOrder' => $stylePrintOrder, + 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both', + 'verticalCentered' => $centered === 'vertical' || $centered === 'both', + // margin size is already stored in inches, so no UOM conversion is required + 'marginLeft' => (float) $marginLeft ?? 0.7, + 'marginRight' => (float) $marginRight ?? 0.7, + 'marginTop' => (float) $marginTop ?? 0.3, + 'marginBottom' => (float) $marginBottom ?? 0.3, + 'marginHeader' => (float) $marginHeader ?? 0.45, + 'marginFooter' => (float) $marginFooter ?? 0.45, + ]; + } + } + + private function readStyleMasterLookup(DOMDocument $styleDom): void + { + $styleMasterLookup = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'master-page'); + + foreach ($styleMasterLookup as $styleMasterSet) { + $styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name'); + $pageLayoutName = $styleMasterSet->getAttributeNS($this->stylesNs, 'page-layout-name'); + $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName; + } + } + + public function readStyleCrossReferences(DOMDocument $contentDom): void + { + $styleXReferences = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'style'); + + foreach ($styleXReferences as $styleXreferenceSet) { + $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name'); + $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name'); + if (!empty($stylePageLayoutName)) { + $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName; + } + } + } + + public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void + { + if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { + return; + } + $masterStyleName = $this->masterStylesCrossReference[$styleName]; + + if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) { + return; + } + $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName]; + + if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) { + return; + } + $printSettings = $this->pageLayoutStyles[$printSettingsIndex]; + + $worksheet->getPageSetup() + ->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT) + ->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) + ->setScale((int) trim($printSettings->scale, '%')) + ->setHorizontalCentered($printSettings->horizontalCentered) + ->setVerticalCentered($printSettings->verticalCentered); + + $worksheet->getPageMargins() + ->setLeft($printSettings->marginLeft) + ->setRight($printSettings->marginRight) + ->setTop($printSettings->marginTop) + ->setBottom($printSettings->marginBottom) + ->setHeader($printSettings->marginHeader) + ->setFooter($printSettings->marginFooter); + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php index c5c7caf8439..d0a45e6adb7 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Ods; use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use SimpleXMLElement; class Properties { @@ -14,7 +15,7 @@ class Properties $this->spreadsheet = $spreadsheet; } - public function load(\SimpleXMLElement $xml, $namespacesMeta) + public function load(SimpleXMLElement $xml, $namespacesMeta): void { $docProps = $this->spreadsheet->getProperties(); $officeProperty = $xml->children($namespacesMeta['office']); @@ -35,7 +36,7 @@ class Properties } } - private function setCoreProperties(DocumentProperties $docProps, \SimpleXMLElement $officePropertyDC) + private function setCoreProperties(DocumentProperties $docProps, SimpleXMLElement $officePropertyDC): void { foreach ($officePropertyDC as $propertyName => $propertyValue) { $propertyValue = (string) $propertyValue; @@ -53,15 +54,11 @@ class Properties $docProps->setLastModifiedBy($propertyValue); break; - case 'creation-date': + case 'date': $creationDate = strtotime($propertyValue); $docProps->setCreated($creationDate); $docProps->setModified($creationDate); - break; - case 'keyword': - $docProps->setKeywords($propertyValue); - break; case 'description': $docProps->setDescription($propertyValue); @@ -73,10 +70,10 @@ class Properties private function setMetaProperties( $namespacesMeta, - \SimpleXMLElement $propertyValue, + SimpleXMLElement $propertyValue, $propertyName, DocumentProperties $docProps - ) { + ): void { $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']); $propertyValue = (string) $propertyValue; switch ($propertyName) { @@ -100,7 +97,7 @@ class Properties } } - private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps) + private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps): void { $propertyValueName = ''; $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php index 732f0bf612c..a65797c1565 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php @@ -61,9 +61,9 @@ class XmlScanner return false; } - private function disableEntityLoaderCheck() + private function disableEntityLoaderCheck(): void { - if (Settings::getLibXmlDisableEntityLoader()) { + if (Settings::getLibXmlDisableEntityLoader() && \PHP_VERSION_ID < 80000) { $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true); if (self::$libxmlDisableEntityLoaderValue === null) { @@ -72,9 +72,9 @@ class XmlScanner } } - public static function shutdown() + public static function shutdown(): void { - if (self::$libxmlDisableEntityLoaderValue !== null) { + if (self::$libxmlDisableEntityLoaderValue !== null && \PHP_VERSION_ID < 80000) { libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue); self::$libxmlDisableEntityLoaderValue = null; } @@ -85,7 +85,7 @@ class XmlScanner self::shutdown(); } - public function setAdditionalCallback(callable $callback) + public function setAdditionalCallback(callable $callback): void { $this->callback = $callback; } @@ -114,8 +114,6 @@ class XmlScanner * * @param mixed $xml * - * @throws Reader\Exception - * * @return string */ public function scan($xml) @@ -143,8 +141,6 @@ class XmlScanner * * @param string $filestream * - * @throws Reader\Exception - * * @return string */ public function scanFile($filestream) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php index d73f598cf95..e58ff2f6c5d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php @@ -2,11 +2,14 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use InvalidArgumentException; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class Slk extends BaseReader { @@ -38,6 +41,20 @@ class Slk extends BaseReader */ private $format = 0; + /** + * Fonts. + * + * @var array + */ + private $fonts = []; + + /** + * Font Count. + * + * @var int + */ + private $fontcount = 0; + /** * Create a new SYLK Reader instance. */ @@ -55,10 +72,9 @@ class Slk extends BaseReader */ public function canRead($pFilename) { - // Check if file exists try { $this->openFile($pFilename); - } catch (Exception $e) { + } catch (InvalidArgumentException $e) { return false; } @@ -78,12 +94,24 @@ class Slk extends BaseReader return $hasDelimiter && $hasId; } + private function canReadOrBust(string $pFilename): void + { + if (!$this->canRead($pFilename)) { + throw new ReaderException($pFilename . ' is an Invalid SYLK file.'); + } + $this->openFile($pFilename); + } + /** * Set input encoding. * + * @deprecated no use is made of this property + * * @param string $pValue Input encoding, eg: 'ANSI' * - * @return Slk + * @return $this + * + * @codeCoverageIgnore */ public function setInputEncoding($pValue) { @@ -95,7 +123,11 @@ class Slk extends BaseReader /** * Get input encoding. * + * @deprecated no use is made of this property + * * @return string + * + * @codeCoverageIgnore */ public function getInputEncoding() { @@ -107,29 +139,21 @@ class Slk extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetInfo($pFilename) { // Open file - if (!$this->canRead($pFilename)) { - throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); - } - $this->openFile($pFilename); + $this->canReadOrBust($pFilename); $fileHandle = $this->fileHandle; rewind($fileHandle); $worksheetInfo = []; - $worksheetInfo[0]['worksheetName'] = 'Worksheet'; - $worksheetInfo[0]['lastColumnLetter'] = 'A'; - $worksheetInfo[0]['lastColumnIndex'] = 0; - $worksheetInfo[0]['totalRows'] = 0; - $worksheetInfo[0]['totalColumns'] = 0; + $worksheetInfo[0]['worksheetName'] = basename($pFilename, '.slk'); // loop through one row (line) at a time in the file $rowIndex = 0; + $columnIndex = 0; while (($rowData = fgets($fileHandle)) !== false) { $columnIndex = 0; @@ -141,28 +165,26 @@ class Slk extends BaseReader $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData))))); $dataType = array_shift($rowData); - if ($dataType == 'C') { - // Read cell value data + if ($dataType == 'B') { foreach ($rowData as $rowDatum) { switch ($rowDatum[0]) { - case 'C': case 'X': $columnIndex = substr($rowDatum, 1) - 1; break; - case 'R': case 'Y': $rowIndex = substr($rowDatum, 1); break; } - - $worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex); - $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex); } + + break; } } + $worksheetInfo[0]['lastColumnIndex'] = $columnIndex; + $worksheetInfo[0]['totalRows'] = $rowIndex; $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1); $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1; @@ -177,8 +199,6 @@ class Slk extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -190,23 +210,305 @@ class Slk extends BaseReader return $this->loadIntoExisting($pFilename, $spreadsheet); } + private $colorArray = [ + 'FF00FFFF', // 0 - cyan + 'FF000000', // 1 - black + 'FFFFFFFF', // 2 - white + 'FFFF0000', // 3 - red + 'FF00FF00', // 4 - green + 'FF0000FF', // 5 - blue + 'FFFFFF00', // 6 - yellow + 'FFFF00FF', // 7 - magenta + ]; + + private $fontStyleMappings = [ + 'B' => 'bold', + 'I' => 'italic', + 'U' => 'underline', + ]; + + private function processFormula(string $rowDatum, bool &$hasCalculatedValue, string &$cellDataFormula, string $row, string $column): void + { + $cellDataFormula = '=' . substr($rowDatum, 1); + // Convert R1C1 style references to A1 style references (but only when not quoted) + $temp = explode('"', $cellDataFormula); + $key = false; + foreach ($temp as &$value) { + // Only count/replace in alternate array entries + if ($key = !$key) { + preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); + // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way + // through the formula from left to right. Reversing means that we work right to left.through + // the formula + $cellReferences = array_reverse($cellReferences); + // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, + // then modify the formula to use that new reference + foreach ($cellReferences as $cellReference) { + $rowReference = $cellReference[2][0]; + // Empty R reference is the current row + if ($rowReference == '') { + $rowReference = $row; + } + // Bracketed R references are relative to the current row + if ($rowReference[0] == '[') { + $rowReference = $row + trim($rowReference, '[]'); + } + $columnReference = $cellReference[4][0]; + // Empty C reference is the current column + if ($columnReference == '') { + $columnReference = $column; + } + // Bracketed C references are relative to the current column + if ($columnReference[0] == '[') { + $columnReference = $column + trim($columnReference, '[]'); + } + $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference; + + $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0])); + } + } + } + unset($value); + // Then rebuild the formula string + $cellDataFormula = implode('"', $temp); + $hasCalculatedValue = true; + } + + private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void + { + // Read cell value data + $hasCalculatedValue = false; + $cellDataFormula = $cellData = ''; + foreach ($rowData as $rowDatum) { + switch ($rowDatum[0]) { + case 'C': + case 'X': + $column = substr($rowDatum, 1); + + break; + case 'R': + case 'Y': + $row = substr($rowDatum, 1); + + break; + case 'K': + $cellData = substr($rowDatum, 1); + + break; + case 'E': + $this->processFormula($rowDatum, $hasCalculatedValue, $cellDataFormula, $row, $column); + + break; + } + } + $columnLetter = Coordinate::stringFromColumnIndex((int) $column); + $cellData = Calculation::unwrapResult($cellData); + + // Set cell value + $this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row"); + } + + private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate): void + { + // Set cell value + $spreadsheet->getActiveSheet()->getCell($coordinate)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData); + if ($hasCalculatedValue) { + $cellData = Calculation::unwrapResult($cellData); + $spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData); + } + } + + private function processFRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void + { + // Read cell formatting + $formatStyle = $columnWidth = ''; + $startCol = $endCol = ''; + $fontStyle = ''; + $styleData = []; + foreach ($rowData as $rowDatum) { + switch ($rowDatum[0]) { + case 'C': + case 'X': + $column = substr($rowDatum, 1); + + break; + case 'R': + case 'Y': + $row = substr($rowDatum, 1); + + break; + case 'P': + $formatStyle = $rowDatum; + + break; + case 'W': + [$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1)); + + break; + case 'S': + $this->styleSettings($rowDatum, $styleData, $fontStyle); + + break; + } + } + $this->addFormats($spreadsheet, $formatStyle, $row, $column); + $this->addFonts($spreadsheet, $fontStyle, $row, $column); + $this->addStyle($spreadsheet, $styleData, $row, $column); + $this->addWidth($spreadsheet, $columnWidth, $startCol, $endCol); + } + + private $styleSettingsFont = ['D' => 'bold', 'I' => 'italic']; + + private $styleSettingsBorder = [ + 'B' => 'bottom', + 'L' => 'left', + 'R' => 'right', + 'T' => 'top', + ]; + + private function styleSettings(string $rowDatum, array &$styleData, string &$fontStyle): void + { + $styleSettings = substr($rowDatum, 1); + $iMax = strlen($styleSettings); + for ($i = 0; $i < $iMax; ++$i) { + $char = $styleSettings[$i]; + if (array_key_exists($char, $this->styleSettingsFont)) { + $styleData['font'][$this->styleSettingsFont[$char]] = true; + } elseif (array_key_exists($char, $this->styleSettingsBorder)) { + $styleData['borders'][$this->styleSettingsBorder[$char]]['borderStyle'] = Border::BORDER_THIN; + } elseif ($char == 'S') { + $styleData['fill']['fillType'] = \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_PATTERN_GRAY125; + } elseif ($char == 'M') { + if (preg_match('/M([1-9]\\d*)/', $styleSettings, $matches)) { + $fontStyle = $matches[1]; + } + } + } + } + + private function addFormats(Spreadsheet &$spreadsheet, string $formatStyle, string $row, string $column): void + { + if ($formatStyle && $column > '' && $row > '') { + $columnLetter = Coordinate::stringFromColumnIndex((int) $column); + if (isset($this->formats[$formatStyle])) { + $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]); + } + } + } + + private function addFonts(Spreadsheet &$spreadsheet, string $fontStyle, string $row, string $column): void + { + if ($fontStyle && $column > '' && $row > '') { + $columnLetter = Coordinate::stringFromColumnIndex((int) $column); + if (isset($this->fonts[$fontStyle])) { + $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->fonts[$fontStyle]); + } + } + } + + private function addStyle(Spreadsheet &$spreadsheet, array $styleData, string $row, string $column): void + { + if ((!empty($styleData)) && $column > '' && $row > '') { + $columnLetter = Coordinate::stringFromColumnIndex($column); + $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData); + } + } + + private function addWidth(Spreadsheet $spreadsheet, string $columnWidth, string $startCol, string $endCol): void + { + if ($columnWidth > '') { + if ($startCol == $endCol) { + $startCol = Coordinate::stringFromColumnIndex((int) $startCol); + $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth); + } else { + $startCol = Coordinate::stringFromColumnIndex($startCol); + $endCol = Coordinate::stringFromColumnIndex($endCol); + $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth); + do { + $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth); + } while ($startCol != $endCol); + } + } + } + + private function processPRecord(array $rowData, Spreadsheet &$spreadsheet): void + { + // Read shared styles + $formatArray = []; + $fromFormats = ['\-', '\ ']; + $toFormats = ['-', ' ']; + foreach ($rowData as $rowDatum) { + switch ($rowDatum[0]) { + case 'P': + $formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1)); + + break; + case 'E': + case 'F': + $formatArray['font']['name'] = substr($rowDatum, 1); + + break; + case 'M': + $formatArray['font']['size'] = substr($rowDatum, 1) / 20; + + break; + case 'L': + $this->processPColors($rowDatum, $formatArray); + + break; + case 'S': + $this->processPFontStyles($rowDatum, $formatArray); + + break; + } + } + $this->processPFinal($spreadsheet, $formatArray); + } + + private function processPColors(string $rowDatum, array &$formatArray): void + { + if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) { + $fontColor = $matches[1] % 8; + $formatArray['font']['color']['argb'] = $this->colorArray[$fontColor]; + } + } + + private function processPFontStyles(string $rowDatum, array &$formatArray): void + { + $styleSettings = substr($rowDatum, 1); + $iMax = strlen($styleSettings); + for ($i = 0; $i < $iMax; ++$i) { + if (array_key_exists($styleSettings[$i], $this->fontStyleMappings)) { + $formatArray['font'][$this->fontStyleMappings[$styleSettings[$i]]] = true; + } + } + } + + private function processPFinal(Spreadsheet &$spreadsheet, array $formatArray): void + { + if (array_key_exists('numberFormat', $formatArray)) { + $this->formats['P' . $this->format] = $formatArray; + ++$this->format; + } elseif (array_key_exists('font', $formatArray)) { + ++$this->fontcount; + $this->fonts[$this->fontcount] = $formatArray; + if ($this->fontcount === 1) { + $spreadsheet->getDefaultStyle()->applyFromArray($formatArray); + } + } + } + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. * * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception * * @return Spreadsheet */ public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) { // Open file - if (!$this->canRead($pFilename)) { - throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); - } - $this->openFile($pFilename); + $this->canReadOrBust($pFilename); $fileHandle = $this->fileHandle; rewind($fileHandle); @@ -215,251 +517,32 @@ class Slk extends BaseReader $spreadsheet->createSheet(); } $spreadsheet->setActiveSheetIndex($this->sheetIndex); - - $fromFormats = ['\-', '\ ']; - $toFormats = ['-', ' ']; + $spreadsheet->getActiveSheet()->setTitle(substr(basename($pFilename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH)); // Loop through file $column = $row = ''; // loop through one row (line) at a time in the file - while (($rowData = fgets($fileHandle)) !== false) { + while (($rowDataTxt = fgets($fileHandle)) !== false) { // convert SYLK encoded $rowData to UTF-8 - $rowData = StringHelper::SYLKtoUTF8($rowData); + $rowDataTxt = StringHelper::SYLKtoUTF8($rowDataTxt); // explode each row at semicolons while taking into account that literal semicolon (;) // is escaped like this (;;) - $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData))))); + $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowDataTxt))))); $dataType = array_shift($rowData); - // Read shared styles if ($dataType == 'P') { - $formatArray = []; - foreach ($rowData as $rowDatum) { - switch ($rowDatum[0]) { - case 'P': - $formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1)); - - break; - case 'E': - case 'F': - $formatArray['font']['name'] = substr($rowDatum, 1); - - break; - case 'L': - $formatArray['font']['size'] = substr($rowDatum, 1); - - break; - case 'S': - $styleSettings = substr($rowDatum, 1); - $iMax = strlen($styleSettings); - for ($i = 0; $i < $iMax; ++$i) { - switch ($styleSettings[$i]) { - case 'I': - $formatArray['font']['italic'] = true; - - break; - case 'D': - $formatArray['font']['bold'] = true; - - break; - case 'T': - $formatArray['borders']['top']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'B': - $formatArray['borders']['bottom']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'L': - $formatArray['borders']['left']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'R': - $formatArray['borders']['right']['borderStyle'] = Border::BORDER_THIN; - - break; - } - } - - break; - } - } - $this->formats['P' . $this->format++] = $formatArray; - // Read cell value data + // Read shared styles + $this->processPRecord($rowData, $spreadsheet); } elseif ($dataType == 'C') { - $hasCalculatedValue = false; - $cellData = $cellDataFormula = ''; - foreach ($rowData as $rowDatum) { - switch ($rowDatum[0]) { - case 'C': - case 'X': - $column = substr($rowDatum, 1); - - break; - case 'R': - case 'Y': - $row = substr($rowDatum, 1); - - break; - case 'K': - $cellData = substr($rowDatum, 1); - - break; - case 'E': - $cellDataFormula = '=' . substr($rowDatum, 1); - // Convert R1C1 style references to A1 style references (but only when not quoted) - $temp = explode('"', $cellDataFormula); - $key = false; - foreach ($temp as &$value) { - // Only count/replace in alternate array entries - if ($key = !$key) { - preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); - // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way - // through the formula from left to right. Reversing means that we work right to left.through - // the formula - $cellReferences = array_reverse($cellReferences); - // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, - // then modify the formula to use that new reference - foreach ($cellReferences as $cellReference) { - $rowReference = $cellReference[2][0]; - // Empty R reference is the current row - if ($rowReference == '') { - $rowReference = $row; - } - // Bracketed R references are relative to the current row - if ($rowReference[0] == '[') { - $rowReference = $row + trim($rowReference, '[]'); - } - $columnReference = $cellReference[4][0]; - // Empty C reference is the current column - if ($columnReference == '') { - $columnReference = $column; - } - // Bracketed C references are relative to the current column - if ($columnReference[0] == '[') { - $columnReference = $column + trim($columnReference, '[]'); - } - $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference; - - $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0])); - } - } - } - unset($value); - // Then rebuild the formula string - $cellDataFormula = implode('"', $temp); - $hasCalculatedValue = true; - - break; - } - } - $columnLetter = Coordinate::stringFromColumnIndex($column); - $cellData = Calculation::unwrapResult($cellData); - - // Set cell value - $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData); - if ($hasCalculatedValue) { - $cellData = Calculation::unwrapResult($cellData); - $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setCalculatedValue($cellData); - } - // Read cell formatting + // Read cell value data + $this->processCRecord($rowData, $spreadsheet, $row, $column); } elseif ($dataType == 'F') { - $formatStyle = $columnWidth = $styleSettings = ''; - $styleData = []; - foreach ($rowData as $rowDatum) { - switch ($rowDatum[0]) { - case 'C': - case 'X': - $column = substr($rowDatum, 1); - - break; - case 'R': - case 'Y': - $row = substr($rowDatum, 1); - - break; - case 'P': - $formatStyle = $rowDatum; - - break; - case 'W': - [$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1)); - - break; - case 'S': - $styleSettings = substr($rowDatum, 1); - $iMax = strlen($styleSettings); - for ($i = 0; $i < $iMax; ++$i) { - switch ($styleSettings[$i]) { - case 'I': - $styleData['font']['italic'] = true; - - break; - case 'D': - $styleData['font']['bold'] = true; - - break; - case 'T': - $styleData['borders']['top']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'B': - $styleData['borders']['bottom']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'L': - $styleData['borders']['left']['borderStyle'] = Border::BORDER_THIN; - - break; - case 'R': - $styleData['borders']['right']['borderStyle'] = Border::BORDER_THIN; - - break; - } - } - - break; - } - } - if (($formatStyle > '') && ($column > '') && ($row > '')) { - $columnLetter = Coordinate::stringFromColumnIndex($column); - if (isset($this->formats[$formatStyle])) { - $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]); - } - } - if ((!empty($styleData)) && ($column > '') && ($row > '')) { - $columnLetter = Coordinate::stringFromColumnIndex($column); - $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData); - } - if ($columnWidth > '') { - if ($startCol == $endCol) { - $startCol = Coordinate::stringFromColumnIndex($startCol); - $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth); - } else { - $startCol = Coordinate::stringFromColumnIndex($startCol); - $endCol = Coordinate::stringFromColumnIndex($endCol); - $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth); - do { - $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth); - } while ($startCol != $endCol); - } - } + // Read cell formatting + $this->processFRecord($rowData, $spreadsheet, $row, $column); } else { - foreach ($rowData as $rowDatum) { - switch ($rowDatum[0]) { - case 'C': - case 'X': - $column = substr($rowDatum, 1); - - break; - case 'R': - case 'Y': - $row = substr($rowDatum, 1); - - break; - } - } + $this->columnRowFromRowData($rowData, $column, $row); } } @@ -470,6 +553,18 @@ class Slk extends BaseReader return $spreadsheet; } + private function columnRowFromRowData(array $rowData, string &$column, string &$row): void + { + foreach ($rowData as $rowDatum) { + $char0 = $rowDatum[0]; + if ($char0 === 'X' || $char0 == 'C') { + $column = substr($rowDatum, 1); + } elseif ($char0 === 'Y' || $char0 == 'R') { + $row = substr($rowDatum, 1); + } + } + } + /** * Get sheet index. * @@ -485,7 +580,7 @@ class Slk extends BaseReader * * @param int $pValue Sheet index * - * @return Slk + * @return $this */ public function setSheetIndex($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php new file mode 100644 index 00000000000..023806d6c20 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php @@ -0,0 +1,7977 @@ +data. + * + * @var int + */ + private $dataSize; + + /** + * Current position in stream. + * + * @var int + */ + private $pos; + + /** + * Workbook to be returned by the reader. + * + * @var Spreadsheet + */ + private $spreadsheet; + + /** + * Worksheet that is currently being built by the reader. + * + * @var Worksheet + */ + private $phpSheet; + + /** + * BIFF version. + * + * @var int + */ + private $version; + + /** + * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) + * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'. + * + * @var string + */ + private $codepage; + + /** + * Shared formats. + * + * @var array + */ + private $formats; + + /** + * Shared fonts. + * + * @var array + */ + private $objFonts; + + /** + * Color palette. + * + * @var array + */ + private $palette; + + /** + * Worksheets. + * + * @var array + */ + private $sheets; + + /** + * External books. + * + * @var array + */ + private $externalBooks; + + /** + * REF structures. Only applies to BIFF8. + * + * @var array + */ + private $ref; + + /** + * External names. + * + * @var array + */ + private $externalNames; + + /** + * Defined names. + * + * @var array + */ + private $definedname; + + /** + * Shared strings. Only applies to BIFF8. + * + * @var array + */ + private $sst; + + /** + * Panes are frozen? (in sheet currently being read). See WINDOW2 record. + * + * @var bool + */ + private $frozen; + + /** + * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record. + * + * @var bool + */ + private $isFitToPages; + + /** + * Objects. One OBJ record contributes with one entry. + * + * @var array + */ + private $objs; + + /** + * Text Objects. One TXO record corresponds with one entry. + * + * @var array + */ + private $textObjects; + + /** + * Cell Annotations (BIFF8). + * + * @var array + */ + private $cellNotes; + + /** + * The combined MSODRAWINGGROUP data. + * + * @var string + */ + private $drawingGroupData; + + /** + * The combined MSODRAWING data (per sheet). + * + * @var string + */ + private $drawingData; + + /** + * Keep track of XF index. + * + * @var int + */ + private $xfIndex; + + /** + * Mapping of XF index (that is a cell XF) to final index in cellXf collection. + * + * @var array + */ + private $mapCellXfIndex; + + /** + * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection. + * + * @var array + */ + private $mapCellStyleXfIndex; + + /** + * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value. + * + * @var array + */ + private $sharedFormulas; + + /** + * The shared formula parts in a sheet. One FORMULA record contributes with one value if it + * refers to a shared formula. + * + * @var array + */ + private $sharedFormulaParts; + + /** + * The type of encryption in use. + * + * @var int + */ + private $encryption = 0; + + /** + * The position in the stream after which contents are encrypted. + * + * @var int + */ + private $encryptionStartPos = false; + + /** + * The current RC4 decryption object. + * + * @var Xls\RC4 + */ + private $rc4Key; + + /** + * The position in the stream that the RC4 decryption object was left at. + * + * @var int + */ + private $rc4Pos = 0; + + /** + * The current MD5 context state. + * + * @var string + */ + private $md5Ctxt; + + /** + * @var int + */ + private $textObjRef; + + /** + * @var string + */ + private $baseCell; + + /** + * Create a new Xls Reader instance. + */ + public function __construct() + { + parent::__construct(); + } + + /** + * Can the current IReader read the file? + * + * @param string $pFilename + * + * @return bool + */ + public function canRead($pFilename) + { + File::assertFile($pFilename); + + try { + // Use ParseXL for the hard work. + $ole = new OLERead(); + + // get excel data + $ole->read($pFilename); + + return true; + } catch (PhpSpreadsheetException $e) { + return false; + } + } + + public function setCodepage(string $codepage): void + { + if (!CodePage::validate($codepage)) { + throw new PhpSpreadsheetException('Unknown codepage: ' . $codepage); + } + + $this->codepage = $codepage; + } + + /** + * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object. + * + * @param string $pFilename + * + * @return array + */ + public function listWorksheetNames($pFilename) + { + File::assertFile($pFilename); + + $worksheetNames = []; + + // Read the OLE file + $this->loadOLE($pFilename); + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->dataSize = strlen($this->data); + + $this->pos = 0; + $this->sheets = []; + + // Parse Workbook Global Substream + while ($this->pos < $this->dataSize) { + $code = self::getUInt2d($this->data, $this->pos); + + switch ($code) { + case self::XLS_TYPE_BOF: + $this->readBof(); + + break; + case self::XLS_TYPE_SHEET: + $this->readSheet(); + + break; + case self::XLS_TYPE_EOF: + $this->readDefault(); + + break 2; + default: + $this->readDefault(); + + break; + } + } + + foreach ($this->sheets as $sheet) { + if ($sheet['sheetType'] != 0x00) { + // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module + continue; + } + + $worksheetNames[] = $sheet['name']; + } + + return $worksheetNames; + } + + /** + * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns). + * + * @param string $pFilename + * + * @return array + */ + public function listWorksheetInfo($pFilename) + { + File::assertFile($pFilename); + + $worksheetInfo = []; + + // Read the OLE file + $this->loadOLE($pFilename); + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->dataSize = strlen($this->data); + + // initialize + $this->pos = 0; + $this->sheets = []; + + // Parse Workbook Global Substream + while ($this->pos < $this->dataSize) { + $code = self::getUInt2d($this->data, $this->pos); + + switch ($code) { + case self::XLS_TYPE_BOF: + $this->readBof(); + + break; + case self::XLS_TYPE_SHEET: + $this->readSheet(); + + break; + case self::XLS_TYPE_EOF: + $this->readDefault(); + + break 2; + default: + $this->readDefault(); + + break; + } + } + + // Parse the individual sheets + foreach ($this->sheets as $sheet) { + if ($sheet['sheetType'] != 0x00) { + // 0x00: Worksheet + // 0x02: Chart + // 0x06: Visual Basic module + continue; + } + + $tmpInfo = []; + $tmpInfo['worksheetName'] = $sheet['name']; + $tmpInfo['lastColumnLetter'] = 'A'; + $tmpInfo['lastColumnIndex'] = 0; + $tmpInfo['totalRows'] = 0; + $tmpInfo['totalColumns'] = 0; + + $this->pos = $sheet['offset']; + + while ($this->pos <= $this->dataSize - 4) { + $code = self::getUInt2d($this->data, $this->pos); + + switch ($code) { + case self::XLS_TYPE_RK: + case self::XLS_TYPE_LABELSST: + case self::XLS_TYPE_NUMBER: + case self::XLS_TYPE_FORMULA: + case self::XLS_TYPE_BOOLERR: + case self::XLS_TYPE_LABEL: + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + $rowIndex = self::getUInt2d($recordData, 0) + 1; + $columnIndex = self::getUInt2d($recordData, 2); + + $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex); + $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex); + + break; + case self::XLS_TYPE_BOF: + $this->readBof(); + + break; + case self::XLS_TYPE_EOF: + $this->readDefault(); + + break 2; + default: + $this->readDefault(); + + break; + } + } + + $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); + $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1; + + $worksheetInfo[] = $tmpInfo; + } + + return $worksheetInfo; + } + + /** + * Loads PhpSpreadsheet from file. + * + * @param string $pFilename + * + * @return Spreadsheet + */ + public function load($pFilename) + { + // Read the OLE file + $this->loadOLE($pFilename); + + // Initialisations + $this->spreadsheet = new Spreadsheet(); + $this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet + if (!$this->readDataOnly) { + $this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style + $this->spreadsheet->removeCellXfByIndex(0); // remove the default style + } + + // Read the summary information stream (containing meta data) + $this->readSummaryInformation(); + + // Read the Additional document summary information stream (containing application-specific meta data) + $this->readDocumentSummaryInformation(); + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->dataSize = strlen($this->data); + + // initialize + $this->pos = 0; + $this->codepage = $this->codepage ?: CodePage::DEFAULT_CODE_PAGE; + $this->formats = []; + $this->objFonts = []; + $this->palette = []; + $this->sheets = []; + $this->externalBooks = []; + $this->ref = []; + $this->definedname = []; + $this->sst = []; + $this->drawingGroupData = ''; + $this->xfIndex = ''; + $this->mapCellXfIndex = []; + $this->mapCellStyleXfIndex = []; + + // Parse Workbook Global Substream + while ($this->pos < $this->dataSize) { + $code = self::getUInt2d($this->data, $this->pos); + + switch ($code) { + case self::XLS_TYPE_BOF: + $this->readBof(); + + break; + case self::XLS_TYPE_FILEPASS: + $this->readFilepass(); + + break; + case self::XLS_TYPE_CODEPAGE: + $this->readCodepage(); + + break; + case self::XLS_TYPE_DATEMODE: + $this->readDateMode(); + + break; + case self::XLS_TYPE_FONT: + $this->readFont(); + + break; + case self::XLS_TYPE_FORMAT: + $this->readFormat(); + + break; + case self::XLS_TYPE_XF: + $this->readXf(); + + break; + case self::XLS_TYPE_XFEXT: + $this->readXfExt(); + + break; + case self::XLS_TYPE_STYLE: + $this->readStyle(); + + break; + case self::XLS_TYPE_PALETTE: + $this->readPalette(); + + break; + case self::XLS_TYPE_SHEET: + $this->readSheet(); + + break; + case self::XLS_TYPE_EXTERNALBOOK: + $this->readExternalBook(); + + break; + case self::XLS_TYPE_EXTERNNAME: + $this->readExternName(); + + break; + case self::XLS_TYPE_EXTERNSHEET: + $this->readExternSheet(); + + break; + case self::XLS_TYPE_DEFINEDNAME: + $this->readDefinedName(); + + break; + case self::XLS_TYPE_MSODRAWINGGROUP: + $this->readMsoDrawingGroup(); + + break; + case self::XLS_TYPE_SST: + $this->readSst(); + + break; + case self::XLS_TYPE_EOF: + $this->readDefault(); + + break 2; + default: + $this->readDefault(); + + break; + } + } + + // Resolve indexed colors for font, fill, and border colors + // Cannot be resolved already in XF record, because PALETTE record comes afterwards + if (!$this->readDataOnly) { + foreach ($this->objFonts as $objFont) { + if (isset($objFont->colorIndex)) { + $color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version); + $objFont->getColor()->setRGB($color['rgb']); + } + } + + foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) { + // fill start and end color + $fill = $objStyle->getFill(); + + if (isset($fill->startcolorIndex)) { + $startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version); + $fill->getStartColor()->setRGB($startColor['rgb']); + } + if (isset($fill->endcolorIndex)) { + $endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version); + $fill->getEndColor()->setRGB($endColor['rgb']); + } + + // border colors + $top = $objStyle->getBorders()->getTop(); + $right = $objStyle->getBorders()->getRight(); + $bottom = $objStyle->getBorders()->getBottom(); + $left = $objStyle->getBorders()->getLeft(); + $diagonal = $objStyle->getBorders()->getDiagonal(); + + if (isset($top->colorIndex)) { + $borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version); + $top->getColor()->setRGB($borderTopColor['rgb']); + } + if (isset($right->colorIndex)) { + $borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version); + $right->getColor()->setRGB($borderRightColor['rgb']); + } + if (isset($bottom->colorIndex)) { + $borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version); + $bottom->getColor()->setRGB($borderBottomColor['rgb']); + } + if (isset($left->colorIndex)) { + $borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version); + $left->getColor()->setRGB($borderLeftColor['rgb']); + } + if (isset($diagonal->colorIndex)) { + $borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version); + $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']); + } + } + } + + // treat MSODRAWINGGROUP records, workbook-level Escher + if (!$this->readDataOnly && $this->drawingGroupData) { + $escherWorkbook = new Escher(); + $reader = new Xls\Escher($escherWorkbook); + $escherWorkbook = $reader->load($this->drawingGroupData); + } + + // Parse the individual sheets + foreach ($this->sheets as $sheet) { + if ($sheet['sheetType'] != 0x00) { + // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module + continue; + } + + // check if sheet should be skipped + if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) { + continue; + } + + // add sheet to PhpSpreadsheet object + $this->phpSheet = $this->spreadsheet->createSheet(); + // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula + // cells... during the load, all formulae should be correct, and we're simply bringing the worksheet + // name in line with the formula, not the reverse + $this->phpSheet->setTitle($sheet['name'], false, false); + $this->phpSheet->setSheetState($sheet['sheetState']); + + $this->pos = $sheet['offset']; + + // Initialize isFitToPages. May change after reading SHEETPR record. + $this->isFitToPages = false; + + // Initialize drawingData + $this->drawingData = ''; + + // Initialize objs + $this->objs = []; + + // Initialize shared formula parts + $this->sharedFormulaParts = []; + + // Initialize shared formulas + $this->sharedFormulas = []; + + // Initialize text objs + $this->textObjects = []; + + // Initialize cell annotations + $this->cellNotes = []; + $this->textObjRef = -1; + + while ($this->pos <= $this->dataSize - 4) { + $code = self::getUInt2d($this->data, $this->pos); + + switch ($code) { + case self::XLS_TYPE_BOF: + $this->readBof(); + + break; + case self::XLS_TYPE_PRINTGRIDLINES: + $this->readPrintGridlines(); + + break; + case self::XLS_TYPE_DEFAULTROWHEIGHT: + $this->readDefaultRowHeight(); + + break; + case self::XLS_TYPE_SHEETPR: + $this->readSheetPr(); + + break; + case self::XLS_TYPE_HORIZONTALPAGEBREAKS: + $this->readHorizontalPageBreaks(); + + break; + case self::XLS_TYPE_VERTICALPAGEBREAKS: + $this->readVerticalPageBreaks(); + + break; + case self::XLS_TYPE_HEADER: + $this->readHeader(); + + break; + case self::XLS_TYPE_FOOTER: + $this->readFooter(); + + break; + case self::XLS_TYPE_HCENTER: + $this->readHcenter(); + + break; + case self::XLS_TYPE_VCENTER: + $this->readVcenter(); + + break; + case self::XLS_TYPE_LEFTMARGIN: + $this->readLeftMargin(); + + break; + case self::XLS_TYPE_RIGHTMARGIN: + $this->readRightMargin(); + + break; + case self::XLS_TYPE_TOPMARGIN: + $this->readTopMargin(); + + break; + case self::XLS_TYPE_BOTTOMMARGIN: + $this->readBottomMargin(); + + break; + case self::XLS_TYPE_PAGESETUP: + $this->readPageSetup(); + + break; + case self::XLS_TYPE_PROTECT: + $this->readProtect(); + + break; + case self::XLS_TYPE_SCENPROTECT: + $this->readScenProtect(); + + break; + case self::XLS_TYPE_OBJECTPROTECT: + $this->readObjectProtect(); + + break; + case self::XLS_TYPE_PASSWORD: + $this->readPassword(); + + break; + case self::XLS_TYPE_DEFCOLWIDTH: + $this->readDefColWidth(); + + break; + case self::XLS_TYPE_COLINFO: + $this->readColInfo(); + + break; + case self::XLS_TYPE_DIMENSION: + $this->readDefault(); + + break; + case self::XLS_TYPE_ROW: + $this->readRow(); + + break; + case self::XLS_TYPE_DBCELL: + $this->readDefault(); + + break; + case self::XLS_TYPE_RK: + $this->readRk(); + + break; + case self::XLS_TYPE_LABELSST: + $this->readLabelSst(); + + break; + case self::XLS_TYPE_MULRK: + $this->readMulRk(); + + break; + case self::XLS_TYPE_NUMBER: + $this->readNumber(); + + break; + case self::XLS_TYPE_FORMULA: + $this->readFormula(); + + break; + case self::XLS_TYPE_SHAREDFMLA: + $this->readSharedFmla(); + + break; + case self::XLS_TYPE_BOOLERR: + $this->readBoolErr(); + + break; + case self::XLS_TYPE_MULBLANK: + $this->readMulBlank(); + + break; + case self::XLS_TYPE_LABEL: + $this->readLabel(); + + break; + case self::XLS_TYPE_BLANK: + $this->readBlank(); + + break; + case self::XLS_TYPE_MSODRAWING: + $this->readMsoDrawing(); + + break; + case self::XLS_TYPE_OBJ: + $this->readObj(); + + break; + case self::XLS_TYPE_WINDOW2: + $this->readWindow2(); + + break; + case self::XLS_TYPE_PAGELAYOUTVIEW: + $this->readPageLayoutView(); + + break; + case self::XLS_TYPE_SCL: + $this->readScl(); + + break; + case self::XLS_TYPE_PANE: + $this->readPane(); + + break; + case self::XLS_TYPE_SELECTION: + $this->readSelection(); + + break; + case self::XLS_TYPE_MERGEDCELLS: + $this->readMergedCells(); + + break; + case self::XLS_TYPE_HYPERLINK: + $this->readHyperLink(); + + break; + case self::XLS_TYPE_DATAVALIDATIONS: + $this->readDataValidations(); + + break; + case self::XLS_TYPE_DATAVALIDATION: + $this->readDataValidation(); + + break; + case self::XLS_TYPE_SHEETLAYOUT: + $this->readSheetLayout(); + + break; + case self::XLS_TYPE_SHEETPROTECTION: + $this->readSheetProtection(); + + break; + case self::XLS_TYPE_RANGEPROTECTION: + $this->readRangeProtection(); + + break; + case self::XLS_TYPE_NOTE: + $this->readNote(); + + break; + case self::XLS_TYPE_TXO: + $this->readTextObject(); + + break; + case self::XLS_TYPE_CONTINUE: + $this->readContinue(); + + break; + case self::XLS_TYPE_EOF: + $this->readDefault(); + + break 2; + default: + $this->readDefault(); + + break; + } + } + + // treat MSODRAWING records, sheet-level Escher + if (!$this->readDataOnly && $this->drawingData) { + $escherWorksheet = new Escher(); + $reader = new Xls\Escher($escherWorksheet); + $escherWorksheet = $reader->load($this->drawingData); + + // get all spContainers in one long array, so they can be mapped to OBJ records + $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers(); + } + + // treat OBJ records + foreach ($this->objs as $n => $obj) { + // the first shape container never has a corresponding OBJ record, hence $n + 1 + if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) { + $spContainer = $allSpContainers[$n + 1]; + + // we skip all spContainers that are a part of a group shape since we cannot yet handle those + if ($spContainer->getNestingLevel() > 1) { + continue; + } + + // calculate the width and height of the shape + [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates()); + [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates()); + + $startOffsetX = $spContainer->getStartOffsetX(); + $startOffsetY = $spContainer->getStartOffsetY(); + $endOffsetX = $spContainer->getEndOffsetX(); + $endOffsetY = $spContainer->getEndOffsetY(); + + $width = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX); + $height = \PhpOffice\PhpSpreadsheet\Shared\Xls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); + + // calculate offsetX and offsetY of the shape + $offsetX = $startOffsetX * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeCol($this->phpSheet, $startColumn) / 1024; + $offsetY = $startOffsetY * \PhpOffice\PhpSpreadsheet\Shared\Xls::sizeRow($this->phpSheet, $startRow) / 256; + + switch ($obj['otObjType']) { + case 0x19: + // Note + if (isset($this->cellNotes[$obj['idObjID']])) { + $cellNote = $this->cellNotes[$obj['idObjID']]; + + if (isset($this->textObjects[$obj['idObjID']])) { + $textObject = $this->textObjects[$obj['idObjID']]; + $this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject; + } + } + + break; + case 0x08: + // picture + // get index to BSE entry (1-based) + $BSEindex = $spContainer->getOPT(0x0104); + + // If there is no BSE Index, we will fail here and other fields are not read. + // Fix by checking here. + // TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field? + // More likely : a uncompatible picture + if (!$BSEindex) { + continue 2; + } + + $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection(); + $BSE = $BSECollection[$BSEindex - 1]; + $blipType = $BSE->getBlipType(); + + // need check because some blip types are not supported by Escher reader such as EMF + if ($blip = $BSE->getBlip()) { + $ih = imagecreatefromstring($blip->getData()); + $drawing = new MemoryDrawing(); + $drawing->setImageResource($ih); + + // width, height, offsetX, offsetY + $drawing->setResizeProportional(false); + $drawing->setWidth($width); + $drawing->setHeight($height); + $drawing->setOffsetX($offsetX); + $drawing->setOffsetY($offsetY); + + switch ($blipType) { + case BSE::BLIPTYPE_JPEG: + $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG); + $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG); + + break; + case BSE::BLIPTYPE_PNG: + $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG); + $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG); + + break; + } + + $drawing->setWorksheet($this->phpSheet); + $drawing->setCoordinates($spContainer->getStartCoordinates()); + } + + break; + default: + // other object type + break; + } + } + } + + // treat SHAREDFMLA records + if ($this->version == self::XLS_BIFF8) { + foreach ($this->sharedFormulaParts as $cell => $baseCell) { + [$column, $row] = Coordinate::coordinateFromString($cell); + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) { + $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell); + $this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA); + } + } + } + + if (!empty($this->cellNotes)) { + foreach ($this->cellNotes as $note => $noteDetails) { + if (!isset($noteDetails['objTextData'])) { + if (isset($this->textObjects[$note])) { + $textObject = $this->textObjects[$note]; + $noteDetails['objTextData'] = $textObject; + } else { + $noteDetails['objTextData']['text'] = ''; + } + } + $cellAddress = str_replace('$', '', $noteDetails['cellRef']); + $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text'])); + } + } + } + + // add the named ranges (defined names) + foreach ($this->definedname as $definedName) { + if ($definedName['isBuiltInName']) { + switch ($definedName['name']) { + case pack('C', 0x06): + // print area + // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2 + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + + $extractedRanges = []; + foreach ($ranges as $range) { + // $range should look like one of these + // Foo!$C$7:$J$66 + // Bar!$A$1:$IV$2 + $explodes = Worksheet::extractSheetTitle($range, true); + $sheetName = trim($explodes[0], "'"); + if (count($explodes) == 2) { + if (strpos($explodes[1], ':') === false) { + $explodes[1] = $explodes[1] . ':' . $explodes[1]; + } + $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66 + } + } + if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) { + $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2 + } + + break; + case pack('C', 0x07): + // print titles (repeating rows) + // Assuming BIFF8, there are 3 cases + // 1. repeating rows + // formula looks like this: Sheet!$A$1:$IV$2 + // rows 1-2 repeat + // 2. repeating columns + // formula looks like this: Sheet!$A$1:$B$65536 + // columns A-B repeat + // 3. both repeating rows and repeating columns + // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2 + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + foreach ($ranges as $range) { + // $range should look like this one of these + // Sheet!$A$1:$B$65536 + // Sheet!$A$1:$IV$2 + if (strpos($range, '!') !== false) { + $explodes = Worksheet::extractSheetTitle($range, true); + if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) { + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $coordinateStrings = explode(':', $extractedRange); + if (count($coordinateStrings) == 2) { + [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]); + [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]); + + if ($firstColumn == 'A' && $lastColumn == 'IV') { + // then we have repeating rows + $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]); + } elseif ($firstRow == 1 && $lastRow == 65536) { + // then we have repeating columns + $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]); + } + } + } + } + } + + break; + } + } else { + // Extract range + if (strpos($definedName['formula'], '!') !== false) { + $explodes = Worksheet::extractSheetTitle($definedName['formula'], true); + if ( + ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) || + ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'"))) + ) { + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $localOnly = ($definedName['scope'] == 0) ? false : true; + + $scope = ($definedName['scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']); + + $this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope)); + } + } + // Named Value + // TODO Provide support for named values + } + } + $this->data = null; + + return $this->spreadsheet; + } + + /** + * Read record data from stream, decrypting as required. + * + * @param string $data Data stream to read from + * @param int $pos Position to start reading from + * @param int $len Record data length + * + * @return string Record data + */ + private function readRecordData($data, $pos, $len) + { + $data = substr($data, $pos, $len); + + // File not encrypted, or record before encryption start point + if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) { + return $data; + } + + $recordData = ''; + if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) { + $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK); + $block = floor($pos / self::REKEY_BLOCK); + $endBlock = floor(($pos + $len) / self::REKEY_BLOCK); + + // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting + // at a point earlier in the current block, re-use it as we can save some time. + if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) { + $this->rc4Key = $this->makeKey($block, $this->md5Ctxt); + $step = $pos % self::REKEY_BLOCK; + } else { + $step = $pos - $this->rc4Pos; + } + $this->rc4Key->RC4(str_repeat("\0", $step)); + + // Decrypt record data (re-keying at the end of every block) + while ($block != $endBlock) { + $step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK); + $recordData .= $this->rc4Key->RC4(substr($data, 0, $step)); + $data = substr($data, $step); + $pos += $step; + $len -= $step; + ++$block; + $this->rc4Key = $this->makeKey($block, $this->md5Ctxt); + } + $recordData .= $this->rc4Key->RC4(substr($data, 0, $len)); + + // Keep track of the position of this decryptor. + // We'll try and re-use it later if we can to speed things up + $this->rc4Pos = $pos + $len; + } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) { + throw new Exception('XOr encryption not supported'); + } + + return $recordData; + } + + /** + * Use OLE reader to extract the relevant data streams from the OLE file. + * + * @param string $pFilename + */ + private function loadOLE($pFilename): void + { + // OLE reader + $ole = new OLERead(); + // get excel data, + $ole->read($pFilename); + // Get workbook data: workbook stream + sheet streams + $this->data = $ole->getStream($ole->wrkbook); + // Get summary information data + $this->summaryInformation = $ole->getStream($ole->summaryInformation); + // Get additional document summary information data + $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation); + } + + /** + * Read summary information. + */ + private function readSummaryInformation(): void + { + if (!isset($this->summaryInformation)) { + return; + } + + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + // offset: 2; size: 2; + // offset: 4; size: 2; OS version + // offset: 6; size: 2; OS indicator + // offset: 8; size: 16 + // offset: 24; size: 4; section count + $secCount = self::getInt4d($this->summaryInformation, 24); + + // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 + // offset: 44; size: 4 + $secOffset = self::getInt4d($this->summaryInformation, 44); + + // section header + // offset: $secOffset; size: 4; section length + $secLength = self::getInt4d($this->summaryInformation, $secOffset); + + // offset: $secOffset+4; size: 4; property count + $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4); + + // initialize code page (used to resolve string values) + $codePage = 'CP1252'; + + // offset: ($secOffset+8); size: var + // loop through property decarations and properties + for ($i = 0; $i < $countProperties; ++$i) { + // offset: ($secOffset+8) + (8 * $i); size: 4; property ID + $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i)); + + // Use value of property id as appropriate + // offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48) + $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i)); + + $type = self::getInt4d($this->summaryInformation, $secOffset + $offset); + + // initialize property value + $value = null; + + // extract property value based on property type + switch ($type) { + case 0x02: // 2 byte signed integer + $value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset); + + break; + case 0x03: // 4 byte signed integer + $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset); + + break; + case 0x13: // 4 byte unsigned integer + // not needed yet, fix later if necessary + break; + case 0x1E: // null-terminated string prepended by dword string length + $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset); + $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength); + $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage); + $value = rtrim($value); + + break; + case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + // PHP-time + $value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8)); + + break; + case 0x47: // Clipboard format + // not needed yet, fix later if necessary + break; + } + + switch ($id) { + case 0x01: // Code Page + $codePage = CodePage::numberToName($value); + + break; + case 0x02: // Title + $this->spreadsheet->getProperties()->setTitle($value); + + break; + case 0x03: // Subject + $this->spreadsheet->getProperties()->setSubject($value); + + break; + case 0x04: // Author (Creator) + $this->spreadsheet->getProperties()->setCreator($value); + + break; + case 0x05: // Keywords + $this->spreadsheet->getProperties()->setKeywords($value); + + break; + case 0x06: // Comments (Description) + $this->spreadsheet->getProperties()->setDescription($value); + + break; + case 0x07: // Template + // Not supported by PhpSpreadsheet + break; + case 0x08: // Last Saved By (LastModifiedBy) + $this->spreadsheet->getProperties()->setLastModifiedBy($value); + + break; + case 0x09: // Revision + // Not supported by PhpSpreadsheet + break; + case 0x0A: // Total Editing Time + // Not supported by PhpSpreadsheet + break; + case 0x0B: // Last Printed + // Not supported by PhpSpreadsheet + break; + case 0x0C: // Created Date/Time + $this->spreadsheet->getProperties()->setCreated($value); + + break; + case 0x0D: // Modified Date/Time + $this->spreadsheet->getProperties()->setModified($value); + + break; + case 0x0E: // Number of Pages + // Not supported by PhpSpreadsheet + break; + case 0x0F: // Number of Words + // Not supported by PhpSpreadsheet + break; + case 0x10: // Number of Characters + // Not supported by PhpSpreadsheet + break; + case 0x11: // Thumbnail + // Not supported by PhpSpreadsheet + break; + case 0x12: // Name of creating application + // Not supported by PhpSpreadsheet + break; + case 0x13: // Security + // Not supported by PhpSpreadsheet + break; + } + } + } + + /** + * Read additional document summary information. + */ + private function readDocumentSummaryInformation(): void + { + if (!isset($this->documentSummaryInformation)) { + return; + } + + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + // offset: 2; size: 2; + // offset: 4; size: 2; OS version + // offset: 6; size: 2; OS indicator + // offset: 8; size: 16 + // offset: 24; size: 4; section count + $secCount = self::getInt4d($this->documentSummaryInformation, 24); + + // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae + // offset: 44; size: 4; first section offset + $secOffset = self::getInt4d($this->documentSummaryInformation, 44); + + // section header + // offset: $secOffset; size: 4; section length + $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset); + + // offset: $secOffset+4; size: 4; property count + $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4); + + // initialize code page (used to resolve string values) + $codePage = 'CP1252'; + + // offset: ($secOffset+8); size: var + // loop through property decarations and properties + for ($i = 0; $i < $countProperties; ++$i) { + // offset: ($secOffset+8) + (8 * $i); size: 4; property ID + $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i)); + + // Use value of property id as appropriate + // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48) + $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i)); + + $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset); + + // initialize property value + $value = null; + + // extract property value based on property type + switch ($type) { + case 0x02: // 2 byte signed integer + $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset); + + break; + case 0x03: // 4 byte signed integer + $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset); + + break; + case 0x0B: // Boolean + $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset); + $value = ($value == 0 ? false : true); + + break; + case 0x13: // 4 byte unsigned integer + // not needed yet, fix later if necessary + break; + case 0x1E: // null-terminated string prepended by dword string length + $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset); + $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength); + $value = StringHelper::convertEncoding($value, 'UTF-8', $codePage); + $value = rtrim($value); + + break; + case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + // PHP-Time + $value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8)); + + break; + case 0x47: // Clipboard format + // not needed yet, fix later if necessary + break; + } + + switch ($id) { + case 0x01: // Code Page + $codePage = CodePage::numberToName($value); + + break; + case 0x02: // Category + $this->spreadsheet->getProperties()->setCategory($value); + + break; + case 0x03: // Presentation Target + // Not supported by PhpSpreadsheet + break; + case 0x04: // Bytes + // Not supported by PhpSpreadsheet + break; + case 0x05: // Lines + // Not supported by PhpSpreadsheet + break; + case 0x06: // Paragraphs + // Not supported by PhpSpreadsheet + break; + case 0x07: // Slides + // Not supported by PhpSpreadsheet + break; + case 0x08: // Notes + // Not supported by PhpSpreadsheet + break; + case 0x09: // Hidden Slides + // Not supported by PhpSpreadsheet + break; + case 0x0A: // MM Clips + // Not supported by PhpSpreadsheet + break; + case 0x0B: // Scale Crop + // Not supported by PhpSpreadsheet + break; + case 0x0C: // Heading Pairs + // Not supported by PhpSpreadsheet + break; + case 0x0D: // Titles of Parts + // Not supported by PhpSpreadsheet + break; + case 0x0E: // Manager + $this->spreadsheet->getProperties()->setManager($value); + + break; + case 0x0F: // Company + $this->spreadsheet->getProperties()->setCompany($value); + + break; + case 0x10: // Links up-to-date + // Not supported by PhpSpreadsheet + break; + } + } + } + + /** + * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record. + */ + private function readDefault(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + + // move stream pointer to next record + $this->pos += 4 + $length; + } + + /** + * The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions, + * this record stores a note (cell note). This feature was significantly enhanced in Excel 97. + */ + private function readNote(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + $cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4)); + if ($this->version == self::XLS_BIFF8) { + $noteObjID = self::getUInt2d($recordData, 6); + $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8)); + $noteAuthor = $noteAuthor['value']; + $this->cellNotes[$noteObjID] = [ + 'cellRef' => $cellAddress, + 'objectID' => $noteObjID, + 'author' => $noteAuthor, + ]; + } else { + $extension = false; + if ($cellAddress == '$B$65536') { + // If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation + // note from the previous cell annotation. We're not yet handling this, so annotations longer than the + // max 2048 bytes will probably throw a wobbly. + $row = self::getUInt2d($recordData, 0); + $extension = true; + $cellAddress = array_pop(array_keys($this->phpSheet->getComments())); + } + + $cellAddress = str_replace('$', '', $cellAddress); + $noteLength = self::getUInt2d($recordData, 4); + $noteText = trim(substr($recordData, 6)); + + if ($extension) { + // Concatenate this extension with the currently set comment for the cell + $comment = $this->phpSheet->getComment($cellAddress); + $commentText = $comment->getText()->getPlainText(); + $comment->setText($this->parseRichText($commentText . $noteText)); + } else { + // Set comment for the cell + $this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText)); +// ->setAuthor($author) + } + } + } + + /** + * The TEXT Object record contains the text associated with a cell annotation. + */ + private function readTextObject(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // recordData consists of an array of subrecords looking like this: + // grbit: 2 bytes; Option Flags + // rot: 2 bytes; rotation + // cchText: 2 bytes; length of the text (in the first continue record) + // cbRuns: 2 bytes; length of the formatting (in the second continue record) + // followed by the continuation records containing the actual text and formatting + $grbitOpts = self::getUInt2d($recordData, 0); + $rot = self::getUInt2d($recordData, 2); + $cchText = self::getUInt2d($recordData, 10); + $cbRuns = self::getUInt2d($recordData, 12); + $text = $this->getSplicedRecordData(); + + $textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1; + $textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte); + // get 1 byte + $is16Bit = ord($text['recordData'][0]); + // it is possible to use a compressed format, + // which omits the high bytes of all characters, if they are all zero + if (($is16Bit & 0x01) === 0) { + $textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1'); + } else { + $textStr = $this->decodeCodepage($textStr); + } + + $this->textObjects[$this->textObjRef] = [ + 'text' => $textStr, + 'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns), + 'alignment' => $grbitOpts, + 'rotation' => $rot, + ]; + } + + /** + * Read BOF. + */ + private function readBof(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = substr($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 2; size: 2; type of the following data + $substreamType = self::getUInt2d($recordData, 2); + + switch ($substreamType) { + case self::XLS_WORKBOOKGLOBALS: + $version = self::getUInt2d($recordData, 0); + if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { + throw new Exception('Cannot read this Excel file. Version is too old.'); + } + $this->version = $version; + + break; + case self::XLS_WORKSHEET: + // do not use this version information for anything + // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream + break; + default: + // substream, e.g. chart + // just skip the entire substream + do { + $code = self::getUInt2d($this->data, $this->pos); + $this->readDefault(); + } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize); + + break; + } + } + + /** + * FILEPASS. + * + * This record is part of the File Protection Block. It + * contains information about the read/write password of the + * file. All record contents following this record will be + * encrypted. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + * + * The decryption functions and objects used from here on in + * are based on the source of Spreadsheet-ParseExcel: + * https://metacpan.org/release/Spreadsheet-ParseExcel + */ + private function readFilepass(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + + if ($length != 54) { + throw new Exception('Unexpected file pass record length'); + } + + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) { + throw new Exception('Decryption password incorrect'); + } + + $this->encryption = self::MS_BIFF_CRYPTO_RC4; + + // Decryption required from the record after next onwards + $this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2); + } + + /** + * Make an RC4 decryptor for the given block. + * + * @param int $block Block for which to create decrypto + * @param string $valContext MD5 context state + * + * @return Xls\RC4 + */ + private function makeKey($block, $valContext) + { + $pwarray = str_repeat("\0", 64); + + for ($i = 0; $i < 5; ++$i) { + $pwarray[$i] = $valContext[$i]; + } + + $pwarray[5] = chr($block & 0xff); + $pwarray[6] = chr(($block >> 8) & 0xff); + $pwarray[7] = chr(($block >> 16) & 0xff); + $pwarray[8] = chr(($block >> 24) & 0xff); + + $pwarray[9] = "\x80"; + $pwarray[56] = "\x48"; + + $md5 = new Xls\MD5(); + $md5->add($pwarray); + + $s = $md5->getContext(); + + return new Xls\RC4($s); + } + + /** + * Verify RC4 file password. + * + * @param string $password Password to check + * @param string $docid Document id + * @param string $salt_data Salt data + * @param string $hashedsalt_data Hashed salt data + * @param string $valContext Set to the MD5 context of the value + * + * @return bool Success + */ + private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext) + { + $pwarray = str_repeat("\0", 64); + + $iMax = strlen($password); + for ($i = 0; $i < $iMax; ++$i) { + $o = ord(substr($password, $i, 1)); + $pwarray[2 * $i] = chr($o & 0xff); + $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff); + } + $pwarray[2 * $i] = chr(0x80); + $pwarray[56] = chr(($i << 4) & 0xff); + + $md5 = new Xls\MD5(); + $md5->add($pwarray); + + $mdContext1 = $md5->getContext(); + + $offset = 0; + $keyoffset = 0; + $tocopy = 5; + + $md5->reset(); + + while ($offset != 16) { + if ((64 - $offset) < 5) { + $tocopy = 64 - $offset; + } + for ($i = 0; $i <= $tocopy; ++$i) { + $pwarray[$offset + $i] = $mdContext1[$keyoffset + $i]; + } + $offset += $tocopy; + + if ($offset == 64) { + $md5->add($pwarray); + $keyoffset = $tocopy; + $tocopy = 5 - $tocopy; + $offset = 0; + + continue; + } + + $keyoffset = 0; + $tocopy = 5; + for ($i = 0; $i < 16; ++$i) { + $pwarray[$offset + $i] = $docid[$i]; + } + $offset += 16; + } + + $pwarray[16] = "\x80"; + for ($i = 0; $i < 47; ++$i) { + $pwarray[17 + $i] = "\0"; + } + $pwarray[56] = "\x80"; + $pwarray[57] = "\x0a"; + + $md5->add($pwarray); + $valContext = $md5->getContext(); + + $key = $this->makeKey(0, $valContext); + + $salt = $key->RC4($salt_data); + $hashedsalt = $key->RC4($hashedsalt_data); + + $salt .= "\x80" . str_repeat("\0", 47); + $salt[56] = "\x80"; + + $md5->reset(); + $md5->add($salt); + $mdContext2 = $md5->getContext(); + + return $mdContext2 == $hashedsalt; + } + + /** + * CODEPAGE. + * + * This record stores the text encoding used to write byte + * strings, stored as MS Windows code page identifier. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readCodepage(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; code page identifier + $codepage = self::getUInt2d($recordData, 0); + + $this->codepage = CodePage::numberToName($codepage); + } + + /** + * DATEMODE. + * + * This record specifies the base date for displaying date + * values. All dates are stored as count of days past this + * base date. In BIFF2-BIFF4 this record is part of the + * Calculation Settings Block. In BIFF5-BIFF8 it is + * stored in the Workbook Globals Substream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readDateMode(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 + Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900); + if (ord($recordData[0]) == 1) { + Date::setExcelCalendar(Date::CALENDAR_MAC_1904); + } + } + + /** + * Read a FONT record. + */ + private function readFont(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + $objFont = new Font(); + + // offset: 0; size: 2; height of the font (in twips = 1/20 of a point) + $size = self::getUInt2d($recordData, 0); + $objFont->setSize($size / 20); + + // offset: 2; size: 2; option flags + // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8) + // bit: 1; mask 0x0002; italic + $isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1; + if ($isItalic) { + $objFont->setItalic(true); + } + + // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8) + // bit: 3; mask 0x0008; strikethrough + $isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3; + if ($isStrike) { + $objFont->setStrikethrough(true); + } + + // offset: 4; size: 2; colour index + $colorIndex = self::getUInt2d($recordData, 4); + $objFont->colorIndex = $colorIndex; + + // offset: 6; size: 2; font weight + $weight = self::getUInt2d($recordData, 6); + switch ($weight) { + case 0x02BC: + $objFont->setBold(true); + + break; + } + + // offset: 8; size: 2; escapement type + $escapement = self::getUInt2d($recordData, 8); + switch ($escapement) { + case 0x0001: + $objFont->setSuperscript(true); + + break; + case 0x0002: + $objFont->setSubscript(true); + + break; + } + + // offset: 10; size: 1; underline type + $underlineType = ord($recordData[10]); + switch ($underlineType) { + case 0x00: + break; // no underline + case 0x01: + $objFont->setUnderline(Font::UNDERLINE_SINGLE); + + break; + case 0x02: + $objFont->setUnderline(Font::UNDERLINE_DOUBLE); + + break; + case 0x21: + $objFont->setUnderline(Font::UNDERLINE_SINGLEACCOUNTING); + + break; + case 0x22: + $objFont->setUnderline(Font::UNDERLINE_DOUBLEACCOUNTING); + + break; + } + + // offset: 11; size: 1; font family + // offset: 12; size: 1; character set + // offset: 13; size: 1; not used + // offset: 14; size: var; font name + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringShort(substr($recordData, 14)); + } else { + $string = $this->readByteStringShort(substr($recordData, 14)); + } + $objFont->setName($string['value']); + + $this->objFonts[] = $objFont; + } + } + + /** + * FORMAT. + * + * This record contains information about a number format. + * All FORMAT records occur together in a sequential list. + * + * In BIFF2-BIFF4 other records referencing a FORMAT record + * contain a zero-based index into this list. From BIFF5 on + * the FORMAT record contains the index itself that will be + * used by other records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readFormat(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + $indexCode = self::getUInt2d($recordData, 0); + + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringLong(substr($recordData, 2)); + } else { + // BIFF7 + $string = $this->readByteStringShort(substr($recordData, 2)); + } + + $formatString = $string['value']; + $this->formats[$indexCode] = $formatString; + } + } + + /** + * XF - Extended Format. + * + * This record contains formatting information for cells, rows, columns or styles. + * According to https://support.microsoft.com/en-us/help/147732 there are always at least 15 cell style XF + * and 1 cell XF. + * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF + * and XF record 15 is a cell XF + * We only read the first cell style XF and skip the remaining cell style XF records + * We read all cell XF records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readXf(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + $objStyle = new Style(); + + if (!$this->readDataOnly) { + // offset: 0; size: 2; Index to FONT record + if (self::getUInt2d($recordData, 0) < 4) { + $fontIndex = self::getUInt2d($recordData, 0); + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = self::getUInt2d($recordData, 0) - 1; + } + $objStyle->setFont($this->objFonts[$fontIndex]); + + // offset: 2; size: 2; Index to FORMAT record + $numberFormatIndex = self::getUInt2d($recordData, 2); + if (isset($this->formats[$numberFormatIndex])) { + // then we have user-defined format code + $numberFormat = ['formatCode' => $this->formats[$numberFormatIndex]]; + } elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') { + // then we have built-in format code + $numberFormat = ['formatCode' => $code]; + } else { + // we set the general format code + $numberFormat = ['formatCode' => 'General']; + } + $objStyle->getNumberFormat()->setFormatCode($numberFormat['formatCode']); + + // offset: 4; size: 2; XF type, cell protection, and parent style XF + // bit 2-0; mask 0x0007; XF_TYPE_PROT + $xfTypeProt = self::getUInt2d($recordData, 4); + // bit 0; mask 0x01; 1 = cell is locked + $isLocked = (0x01 & $xfTypeProt) >> 0; + $objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED); + + // bit 1; mask 0x02; 1 = Formula is hidden + $isHidden = (0x02 & $xfTypeProt) >> 1; + $objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED); + + // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF + $isCellStyleXf = (0x04 & $xfTypeProt) >> 2; + + // offset: 6; size: 1; Alignment and text break + // bit 2-0, mask 0x07; horizontal alignment + $horAlign = (0x07 & ord($recordData[6])) >> 0; + switch ($horAlign) { + case 0: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_GENERAL); + + break; + case 1: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); + + break; + case 2: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + + break; + case 3: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT); + + break; + case 4: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_FILL); + + break; + case 5: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_JUSTIFY); + + break; + case 6: + $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER_CONTINUOUS); + + break; + } + // bit 3, mask 0x08; wrap text + $wrapText = (0x08 & ord($recordData[6])) >> 3; + switch ($wrapText) { + case 0: + $objStyle->getAlignment()->setWrapText(false); + + break; + case 1: + $objStyle->getAlignment()->setWrapText(true); + + break; + } + // bit 6-4, mask 0x70; vertical alignment + $vertAlign = (0x70 & ord($recordData[6])) >> 4; + switch ($vertAlign) { + case 0: + $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_TOP); + + break; + case 1: + $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); + + break; + case 2: + $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM); + + break; + case 3: + $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_JUSTIFY); + + break; + } + + if ($this->version == self::XLS_BIFF8) { + // offset: 7; size: 1; XF_ROTATION: Text rotation angle + $angle = ord($recordData[7]); + $rotation = 0; + if ($angle <= 90) { + $rotation = $angle; + } elseif ($angle <= 180) { + $rotation = 90 - $angle; + } elseif ($angle == 255) { + $rotation = -165; + } + $objStyle->getAlignment()->setTextRotation($rotation); + + // offset: 8; size: 1; Indentation, shrink to cell size, and text direction + // bit: 3-0; mask: 0x0F; indent level + $indent = (0x0F & ord($recordData[8])) >> 0; + $objStyle->getAlignment()->setIndent($indent); + + // bit: 4; mask: 0x10; 1 = shrink content to fit into cell + $shrinkToFit = (0x10 & ord($recordData[8])) >> 4; + switch ($shrinkToFit) { + case 0: + $objStyle->getAlignment()->setShrinkToFit(false); + + break; + case 1: + $objStyle->getAlignment()->setShrinkToFit(true); + + break; + } + + // offset: 9; size: 1; Flags used for attribute groups + + // offset: 10; size: 4; Cell border lines and background area + // bit: 3-0; mask: 0x0000000F; left style + if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) { + $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle); + } + // bit: 7-4; mask: 0x000000F0; right style + if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) { + $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle); + } + // bit: 11-8; mask: 0x00000F00; top style + if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) { + $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle); + } + // bit: 15-12; mask: 0x0000F000; bottom style + if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) { + $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle); + } + // bit: 22-16; mask: 0x007F0000; left color + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16; + + // bit: 29-23; mask: 0x3F800000; right color + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23; + + // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom + $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false; + + // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right + $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false; + + if ($diagonalUp == false && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE); + } elseif ($diagonalUp == true && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP); + } elseif ($diagonalUp == false && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN); + } elseif ($diagonalUp == true && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH); + } + + // offset: 14; size: 4; + // bit: 6-0; mask: 0x0000007F; top color + $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0; + + // bit: 13-7; mask: 0x00003F80; bottom color + $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7; + + // bit: 20-14; mask: 0x001FC000; diagonal color + $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14; + + // bit: 24-21; mask: 0x01E00000; diagonal style + if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) { + $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle); + } + + // bit: 31-26; mask: 0xFC000000 fill pattern + if ($fillType = Xls\Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) { + $objStyle->getFill()->setFillType($fillType); + } + // offset: 18; size: 2; pattern and background colour + // bit: 6-0; mask: 0x007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0; + + // bit: 13-7; mask: 0x3F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7; + } else { + // BIFF5 + + // offset: 7; size: 1; Text orientation and flags + $orientationAndFlags = ord($recordData[7]); + + // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation + $xfOrientation = (0x03 & $orientationAndFlags) >> 0; + switch ($xfOrientation) { + case 0: + $objStyle->getAlignment()->setTextRotation(0); + + break; + case 1: + $objStyle->getAlignment()->setTextRotation(-165); + + break; + case 2: + $objStyle->getAlignment()->setTextRotation(90); + + break; + case 3: + $objStyle->getAlignment()->setTextRotation(-90); + + break; + } + + // offset: 8; size: 4; cell border lines and background area + $borderAndBackground = self::getInt4d($recordData, 8); + + // bit: 6-0; mask: 0x0000007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0; + + // bit: 13-7; mask: 0x00003F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7; + + // bit: 21-16; mask: 0x003F0000; fill pattern + $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16)); + + // bit: 24-22; mask: 0x01C00000; bottom line style + $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22)); + + // bit: 31-25; mask: 0xFE000000; bottom line color + $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25; + + // offset: 12; size: 4; cell border lines + $borderLines = self::getInt4d($recordData, 12); + + // bit: 2-0; mask: 0x00000007; top line style + $objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0)); + + // bit: 5-3; mask: 0x00000038; left line style + $objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3)); + + // bit: 8-6; mask: 0x000001C0; right line style + $objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6)); + + // bit: 15-9; mask: 0x0000FE00; top line color index + $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9; + + // bit: 22-16; mask: 0x007F0000; left line color index + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16; + + // bit: 29-23; mask: 0x3F800000; right line color index + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23; + } + + // add cellStyleXf or cellXf and update mapping + if ($isCellStyleXf) { + // we only read one style XF record which is always the first + if ($this->xfIndex == 0) { + $this->spreadsheet->addCellStyleXf($objStyle); + $this->mapCellStyleXfIndex[$this->xfIndex] = 0; + } + } else { + // we read all cell XF records + $this->spreadsheet->addCellXf($objStyle); + $this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1; + } + + // update XF index for when we read next record + ++$this->xfIndex; + } + } + + private function readXfExt(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; 0x087D = repeated header + + // offset: 2; size: 2 + + // offset: 4; size: 8; not used + + // offset: 12; size: 2; record version + + // offset: 14; size: 2; index to XF record which this record modifies + $ixfe = self::getUInt2d($recordData, 14); + + // offset: 16; size: 2; not used + + // offset: 18; size: 2; number of extension properties that follow + $cexts = self::getUInt2d($recordData, 18); + + // start reading the actual extension data + $offset = 20; + while ($offset < $length) { + // extension type + $extType = self::getUInt2d($recordData, $offset); + + // extension length + $cb = self::getUInt2d($recordData, $offset + 2); + + // extension data + $extData = substr($recordData, $offset + 4, $cb); + + switch ($extType) { + case 4: // fill start color + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill(); + $fill->getStartColor()->setRGB($rgb); + $fill->startcolorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 5: // fill end color + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill(); + $fill->getEndColor()->setRGB($rgb); + $fill->endcolorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 7: // border color top + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop(); + $top->getColor()->setRGB($rgb); + $top->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 8: // border color bottom + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom(); + $bottom->getColor()->setRGB($rgb); + $bottom->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 9: // border color left + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft(); + $left->getColor()->setRGB($rgb); + $left->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 10: // border color right + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight(); + $right->getColor()->setRGB($rgb); + $right->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 11: // border color diagonal + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal(); + $diagonal->getColor()->setRGB($rgb); + $diagonal->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + case 13: // font color + $xclfType = self::getUInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2])); + + // modify the relevant style property + if (isset($this->mapCellXfIndex[$ixfe])) { + $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont(); + $font->getColor()->setRGB($rgb); + $font->colorIndex = null; // normal color index does not apply, discard + } + } + + break; + } + + $offset += $cb; + } + } + } + + /** + * Read STYLE record. + */ + private function readStyle(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; index to XF record and flag for built-in style + $ixfe = self::getUInt2d($recordData, 0); + + // bit: 11-0; mask 0x0FFF; index to XF record + $xfIndex = (0x0FFF & $ixfe) >> 0; + + // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style + $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15); + + if ($isBuiltIn) { + // offset: 2; size: 1; identifier for built-in style + $builtInId = ord($recordData[2]); + + switch ($builtInId) { + case 0x00: + // currently, we are not using this for anything + break; + default: + break; + } + } + // user-defined; not supported by PhpSpreadsheet + } + } + + /** + * Read PALETTE record. + */ + private function readPalette(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; number of following colors + $nm = self::getUInt2d($recordData, 0); + + // list of RGB colors + for ($i = 0; $i < $nm; ++$i) { + $rgb = substr($recordData, 2 + 4 * $i, 4); + $this->palette[] = self::readRGB($rgb); + } + } + } + + /** + * SHEET. + * + * This record is located in the Workbook Globals + * Substream and represents a sheet inside the workbook. + * One SHEET record is written for each sheet. It stores the + * sheet name and a stream offset to the BOF record of the + * respective Sheet Substream within the Workbook Stream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readSheet(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // offset: 0; size: 4; absolute stream position of the BOF record of the sheet + // NOTE: not encrypted + $rec_offset = self::getInt4d($this->data, $this->pos + 4); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 4; size: 1; sheet state + switch (ord($recordData[4])) { + case 0x00: + $sheetState = Worksheet::SHEETSTATE_VISIBLE; + + break; + case 0x01: + $sheetState = Worksheet::SHEETSTATE_HIDDEN; + + break; + case 0x02: + $sheetState = Worksheet::SHEETSTATE_VERYHIDDEN; + + break; + default: + $sheetState = Worksheet::SHEETSTATE_VISIBLE; + + break; + } + + // offset: 5; size: 1; sheet type + $sheetType = ord($recordData[5]); + + // offset: 6; size: var; sheet name + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } elseif ($this->version == self::XLS_BIFF7) { + $string = $this->readByteStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } + + $this->sheets[] = [ + 'name' => $rec_name, + 'offset' => $rec_offset, + 'sheetState' => $sheetState, + 'sheetType' => $sheetType, + ]; + } + + /** + * Read EXTERNALBOOK record. + */ + private function readExternalBook(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset within record data + $offset = 0; + + // there are 4 types of records + if (strlen($recordData) > 4) { + // external reference + // offset: 0; size: 2; number of sheet names ($nm) + $nm = self::getUInt2d($recordData, 0); + $offset += 2; + + // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length) + $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2)); + $offset += $encodedUrlString['size']; + + // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length) + $externalSheetNames = []; + for ($i = 0; $i < $nm; ++$i) { + $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset)); + $externalSheetNames[] = $externalSheetNameString['value']; + $offset += $externalSheetNameString['size']; + } + + // store the record data + $this->externalBooks[] = [ + 'type' => 'external', + 'encodedUrl' => $encodedUrlString['value'], + 'externalSheetNames' => $externalSheetNames, + ]; + } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) { + // internal reference + // offset: 0; size: 2; number of sheet in this document + // offset: 2; size: 2; 0x01 0x04 + $this->externalBooks[] = [ + 'type' => 'internal', + ]; + } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) { + // add-in function + // offset: 0; size: 2; 0x0001 + $this->externalBooks[] = [ + 'type' => 'addInFunction', + ]; + } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) { + // DDE links, OLE links + // offset: 0; size: 2; 0x0000 + // offset: 2; size: var; encoded source document name + $this->externalBooks[] = [ + 'type' => 'DDEorOLE', + ]; + } + } + + /** + * Read EXTERNNAME record. + */ + private function readExternName(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->version == self::XLS_BIFF8) { + // offset: 0; size: 2; options + $options = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; + + // offset: 4; size: 2; not used + + // offset: 6; size: var + $nameString = self::readUnicodeStringShort(substr($recordData, 6)); + + // offset: var; size: var; formula data + $offset = 6 + $nameString['size']; + $formula = $this->getFormulaFromStructure(substr($recordData, $offset)); + + $this->externalNames[] = [ + 'name' => $nameString['value'], + 'formula' => $formula, + ]; + } + } + + /** + * Read EXTERNSHEET record. + */ + private function readExternSheet(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->version == self::XLS_BIFF8) { + // offset: 0; size: 2; number of following ref structures + $nm = self::getUInt2d($recordData, 0); + for ($i = 0; $i < $nm; ++$i) { + $this->ref[] = [ + // offset: 2 + 6 * $i; index to EXTERNALBOOK record + 'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 * $i), + // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record + 'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 * $i), + // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record + 'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 * $i), + ]; + } + } + } + + /** + * DEFINEDNAME. + * + * This record is part of a Link Table. It contains the name + * and the token array of an internal defined name. Token + * arrays of defined names contain tokens with aberrant + * token classes. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readDefinedName(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8) { + // retrieves named cells + + // offset: 0; size: 2; option flags + $opts = self::getUInt2d($recordData, 0); + + // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name + $isBuiltInName = (0x0020 & $opts) >> 5; + + // offset: 2; size: 1; keyboard shortcut + + // offset: 3; size: 1; length of the name (character count) + $nlen = ord($recordData[3]); + + // offset: 4; size: 2; size of the formula data (it can happen that this is zero) + // note: there can also be additional data, this is not included in $flen + $flen = self::getUInt2d($recordData, 4); + + // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based) + $scope = self::getUInt2d($recordData, 8); + + // offset: 14; size: var; Name (Unicode string without length field) + $string = self::readUnicodeString(substr($recordData, 14), $nlen); + + // offset: var; size: $flen; formula data + $offset = 14 + $string['size']; + $formulaStructure = pack('v', $flen) . substr($recordData, $offset); + + try { + $formula = $this->getFormulaFromStructure($formulaStructure); + } catch (PhpSpreadsheetException $e) { + $formula = ''; + } + + $this->definedname[] = [ + 'isBuiltInName' => $isBuiltInName, + 'name' => $string['value'], + 'formula' => $formula, + 'scope' => $scope, + ]; + } + } + + /** + * Read MSODRAWINGGROUP record. + */ + private function readMsoDrawingGroup(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + + // get spliced record data + $splicedRecordData = $this->getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->drawingGroupData .= $recordData; + } + + /** + * SST - Shared String Table. + * + * This record contains a list of all strings used anywhere + * in the workbook. Each string occurs only once. The + * workbook uses indexes into the list to reference the + * strings. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readSst(): void + { + // offset within (spliced) record data + $pos = 0; + + // Limit global SST position, further control for bad SST Length in BIFF8 data + $limitposSST = 0; + + // get spliced record data + $splicedRecordData = $this->getSplicedRecordData(); + + $recordData = $splicedRecordData['recordData']; + $spliceOffsets = $splicedRecordData['spliceOffsets']; + + // offset: 0; size: 4; total number of strings in the workbook + $pos += 4; + + // offset: 4; size: 4; number of following strings ($nm) + $nm = self::getInt4d($recordData, 4); + $pos += 4; + + // look up limit position + foreach ($spliceOffsets as $spliceOffset) { + // it can happen that the string is empty, therefore we need + // <= and not just < + if ($pos <= $spliceOffset) { + $limitposSST = $spliceOffset; + } + } + + // loop through the Unicode strings (16-bit length) + for ($i = 0; $i < $nm && $pos < $limitposSST; ++$i) { + // number of characters in the Unicode string + $numChars = self::getUInt2d($recordData, $pos); + $pos += 2; + + // option flags + $optionFlags = ord($recordData[$pos]); + ++$pos; + + // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed + $isCompressed = (($optionFlags & 0x01) == 0); + + // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic + $hasAsian = (($optionFlags & 0x04) != 0); + + // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text + $hasRichText = (($optionFlags & 0x08) != 0); + + if ($hasRichText) { + // number of Rich-Text formatting runs + $formattingRuns = self::getUInt2d($recordData, $pos); + $pos += 2; + } + + if ($hasAsian) { + // size of Asian phonetic setting + $extendedRunLength = self::getInt4d($recordData, $pos); + $pos += 4; + } + + // expected byte length of character array if not split + $len = ($isCompressed) ? $numChars : $numChars * 2; + + // look up limit position - Check it again to be sure that no error occurs when parsing SST structure + foreach ($spliceOffsets as $spliceOffset) { + // it can happen that the string is empty, therefore we need + // <= and not just < + if ($pos <= $spliceOffset) { + $limitpos = $spliceOffset; + + break; + } + } + + if ($pos + $len <= $limitpos) { + // character array is not split between records + + $retstr = substr($recordData, $pos, $len); + $pos += $len; + } else { + // character array is split between records + + // first part of character array + $retstr = substr($recordData, $pos, $limitpos - $pos); + + $bytesRead = $limitpos - $pos; + + // remaining characters in Unicode string + $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2)); + + $pos = $limitpos; + + // keep reading the characters + while ($charsLeft > 0) { + // look up next limit position, in case the string span more than one continue record + foreach ($spliceOffsets as $spliceOffset) { + if ($pos < $spliceOffset) { + $limitpos = $spliceOffset; + + break; + } + } + + // repeated option flags + // OpenOffice.org documentation 5.21 + $option = ord($recordData[$pos]); + ++$pos; + + if ($isCompressed && ($option == 0)) { + // 1st fragment compressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len; + $isCompressed = true; + } elseif (!$isCompressed && ($option != 0)) { + // 1st fragment uncompressed + // this fragment uncompressed + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + } elseif (!$isCompressed && ($option == 0)) { + // 1st fragment uncompressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + for ($j = 0; $j < $len; ++$j) { + $retstr .= $recordData[$pos + $j] + . chr(0); + } + $charsLeft -= $len; + $isCompressed = false; + } else { + // 1st fragment compressed + // this fragment uncompressed + $newstr = ''; + $jMax = strlen($retstr); + for ($j = 0; $j < $jMax; ++$j) { + $newstr .= $retstr[$j] . chr(0); + } + $retstr = $newstr; + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + } + + $pos += $len; + } + } + + // convert to UTF-8 + $retstr = self::encodeUTF16($retstr, $isCompressed); + + // read additional Rich-Text information, if any + $fmtRuns = []; + if ($hasRichText) { + // list of formatting runs + for ($j = 0; $j < $formattingRuns; ++$j) { + // first formatted character; zero-based + $charPos = self::getUInt2d($recordData, $pos + $j * 4); + + // index to font record + $fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4); + + $fmtRuns[] = [ + 'charPos' => $charPos, + 'fontIndex' => $fontIndex, + ]; + } + $pos += 4 * $formattingRuns; + } + + // read additional Asian phonetics information, if any + if ($hasAsian) { + // For Asian phonetic settings, we skip the extended string data + $pos += $extendedRunLength; + } + + // store the shared sting + $this->sst[] = [ + 'value' => $retstr, + 'fmtRuns' => $fmtRuns, + ]; + } + + // getSplicedRecordData() takes care of moving current position in data stream + } + + /** + * Read PRINTGRIDLINES record. + */ + private function readPrintGridlines(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) { + // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines + $printGridlines = (bool) self::getUInt2d($recordData, 0); + $this->phpSheet->setPrintGridlines($printGridlines); + } + } + + /** + * Read DEFAULTROWHEIGHT record. + */ + private function readDefaultRowHeight(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; option flags + // offset: 2; size: 2; default height for unused rows, (twips 1/20 point) + $height = self::getUInt2d($recordData, 2); + $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20); + } + + /** + * Read SHEETPR record. + */ + private function readSheetPr(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2 + + // bit: 6; mask: 0x0040; 0 = outline buttons above outline group + $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6; + $this->phpSheet->setShowSummaryBelow($isSummaryBelow); + + // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group + $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7; + $this->phpSheet->setShowSummaryRight($isSummaryRight); + + // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages + // this corresponds to radio button setting in page setup dialog in Excel + $this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8); + } + + /** + * Read HORIZONTALPAGEBREAKS record. + */ + private function readHorizontalPageBreaks(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) { + // offset: 0; size: 2; number of the following row index structures + $nm = self::getUInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $r = self::getUInt2d($recordData, 2 + 6 * $i); + $cf = self::getUInt2d($recordData, 2 + 6 * $i + 2); + $cl = self::getUInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two column indexes are necessary? + $this->phpSheet->setBreakByColumnAndRow($cf + 1, $r, Worksheet::BREAK_ROW); + } + } + } + + /** + * Read VERTICALPAGEBREAKS record. + */ + private function readVerticalPageBreaks(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) { + // offset: 0; size: 2; number of the following column index structures + $nm = self::getUInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $c = self::getUInt2d($recordData, 2 + 6 * $i); + $rf = self::getUInt2d($recordData, 2 + 6 * $i + 2); + $rl = self::getUInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two row indexes are necessary? + $this->phpSheet->setBreakByColumnAndRow($c + 1, $rf, Worksheet::BREAK_COLUMN); + } + } + } + + /** + * Read HEADER record. + */ + private function readHeader(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringLong($recordData); + } else { + $string = $this->readByteStringShort($recordData); + } + + $this->phpSheet->getHeaderFooter()->setOddHeader($string['value']); + $this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']); + } + } + } + + /** + * Read FOOTER record. + */ + private function readFooter(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringLong($recordData); + } else { + $string = $this->readByteStringShort($recordData); + } + $this->phpSheet->getHeaderFooter()->setOddFooter($string['value']); + $this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']); + } + } + } + + /** + * Read HCENTER record. + */ + private function readHcenter(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally + $isHorizontalCentered = (bool) self::getUInt2d($recordData, 0); + + $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered); + } + } + + /** + * Read VCENTER record. + */ + private function readVcenter(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered + $isVerticalCentered = (bool) self::getUInt2d($recordData, 0); + + $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered); + } + } + + /** + * Read LEFTMARGIN record. + */ + private function readLeftMargin(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 8 + $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData)); + } + } + + /** + * Read RIGHTMARGIN record. + */ + private function readRightMargin(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 8 + $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData)); + } + } + + /** + * Read TOPMARGIN record. + */ + private function readTopMargin(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 8 + $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData)); + } + } + + /** + * Read BOTTOMMARGIN record. + */ + private function readBottomMargin(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 8 + $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData)); + } + } + + /** + * Read PAGESETUP record. + */ + private function readPageSetup(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; paper size + $paperSize = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; scaling factor + $scale = self::getUInt2d($recordData, 2); + + // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed + $fitToWidth = self::getUInt2d($recordData, 6); + + // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed + $fitToHeight = self::getUInt2d($recordData, 8); + + // offset: 10; size: 2; option flags + + // bit: 0; mask: 0x0001; 0=down then over, 1=over then down + $isOverThenDown = (0x0001 & self::getUInt2d($recordData, 10)); + + // bit: 1; mask: 0x0002; 0=landscape, 1=portrait + $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1; + + // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init + // when this bit is set, do not use flags for those properties + $isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2; + + if (!$isNotInit) { + $this->phpSheet->getPageSetup()->setPaperSize($paperSize); + $this->phpSheet->getPageSetup()->setPageOrder(((bool) $isOverThenDown) ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER); + $this->phpSheet->getPageSetup()->setOrientation(((bool) $isPortrait) ? PageSetup::ORIENTATION_PORTRAIT : PageSetup::ORIENTATION_LANDSCAPE); + + $this->phpSheet->getPageSetup()->setScale($scale, false); + $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages); + $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false); + $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false); + } + + // offset: 16; size: 8; header margin (IEEE 754 floating-point value) + $marginHeader = self::extractNumber(substr($recordData, 16, 8)); + $this->phpSheet->getPageMargins()->setHeader($marginHeader); + + // offset: 24; size: 8; footer margin (IEEE 754 floating-point value) + $marginFooter = self::extractNumber(substr($recordData, 24, 8)); + $this->phpSheet->getPageMargins()->setFooter($marginFooter); + } + } + + /** + * PROTECT - Sheet protection (BIFF2 through BIFF8) + * if this record is omitted, then it also means no sheet protection. + */ + private function readProtect(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit 0, mask 0x01; 1 = sheet is protected + $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0; + $this->phpSheet->getProtection()->setSheet((bool) $bool); + } + + /** + * SCENPROTECT. + */ + private function readScenProtect(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = scenarios are protected + $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0; + + $this->phpSheet->getProtection()->setScenarios((bool) $bool); + } + + /** + * OBJECTPROTECT. + */ + private function readObjectProtect(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = objects are protected + $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0; + + $this->phpSheet->getProtection()->setObjects((bool) $bool); + } + + /** + * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8). + */ + private function readPassword(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; 16-bit hash value of password + $password = strtoupper(dechex(self::getUInt2d($recordData, 0))); // the hashed password + $this->phpSheet->getProtection()->setPassword($password, true); + } + } + + /** + * Read DEFCOLWIDTH record. + */ + private function readDefColWidth(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; default column width + $width = self::getUInt2d($recordData, 0); + if ($width != 8) { + $this->phpSheet->getDefaultColumnDimension()->setWidth($width); + } + } + + /** + * Read COLINFO record. + */ + private function readColInfo(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; index to first column in range + $firstColumnIndex = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to last column in range + $lastColumnIndex = self::getUInt2d($recordData, 2); + + // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character + $width = self::getUInt2d($recordData, 4); + + // offset: 6; size: 2; index to XF record for default column formatting + $xfIndex = self::getUInt2d($recordData, 6); + + // offset: 8; size: 2; option flags + // bit: 0; mask: 0x0001; 1= columns are hidden + $isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0; + + // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline) + $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8; + + // bit: 12; mask: 0x1000; 1 = collapsed + $isCollapsed = (0x1000 & self::getUInt2d($recordData, 8)) >> 12; + + // offset: 10; size: 2; not used + + for ($i = $firstColumnIndex + 1; $i <= $lastColumnIndex + 1; ++$i) { + if ($lastColumnIndex == 255 || $lastColumnIndex == 256) { + $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256); + + break; + } + $this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256); + $this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden); + $this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level); + $this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed); + if (isset($this->mapCellXfIndex[$xfIndex])) { + $this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + } + + /** + * ROW. + * + * This record contains the properties of a single row in a + * sheet. Rows and cells in a sheet are divided into blocks + * of 32 rows. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readRow(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; index of this row + $r = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to column of the first cell which is described by a cell record + + // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1 + + // offset: 6; size: 2; + + // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point + $height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0; + + // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height + $useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15; + + if (!$useDefaultHeight) { + $this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20); + } + + // offset: 8; size: 2; not used + + // offset: 10; size: 2; not used in BIFF5-BIFF8 + + // offset: 12; size: 4; option flags and default row formatting + + // bit: 2-0: mask: 0x00000007; outline level of the row + $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0; + $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); + + // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed + $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4; + $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); + + // bit: 5; mask: 0x00000020; 1 = row is hidden + $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5; + $this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden); + + // bit: 7; mask: 0x00000080; 1 = row has explicit format + $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7; + + // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record + $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16; + + if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) { + $this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read RK record + * This record represents a cell that contains an RK value + * (encoded integer or floating-point value). If a + * floating-point value cannot be encoded to an RK value, + * a NUMBER record will be written. This record replaces the + * record INTEGER written in BIFF2. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readRk(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: 4; size: 2; index to XF record + $xfIndex = self::getUInt2d($recordData, 4); + + // offset: 6; size: 4; RK value + $rknum = self::getInt4d($recordData, 6); + $numValue = self::getIEEE754($rknum); + + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add style information + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + + // add cell + $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC); + } + } + + /** + * Read LABELSST record + * This record represents a cell that contains a string. It + * replaces the LABEL record and RSTRING record used in + * BIFF2-BIFF5. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readLabelSst(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + $emptyCell = true; + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: 4; size: 2; index to XF record + $xfIndex = self::getUInt2d($recordData, 4); + + // offset: 6; size: 4; index to SST record + $index = self::getInt4d($recordData, 6); + + // add cell + if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) { + // then we should treat as rich text + $richText = new RichText(); + $charPos = 0; + $sstCount = count($this->sst[$index]['fmtRuns']); + for ($i = 0; $i <= $sstCount; ++$i) { + if (isset($fmtRuns[$i])) { + $text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos); + $charPos = $fmtRuns[$i]['charPos']; + } else { + $text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value'])); + } + + if (StringHelper::countCharacters($text) > 0) { + if ($i == 0) { // first text run, no style + $richText->createText($text); + } else { + $textRun = $richText->createTextRun($text); + if (isset($fmtRuns[$i - 1])) { + if ($fmtRuns[$i - 1]['fontIndex'] < 4) { + $fontIndex = $fmtRuns[$i - 1]['fontIndex']; + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1; + } + $textRun->setFont(clone $this->objFonts[$fontIndex]); + } + } + } + } + if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') { + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($richText, DataType::TYPE_STRING); + $emptyCell = false; + } + } else { + if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') { + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING); + $emptyCell = false; + } + } + + if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) { + // add style information + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULRK record + * This record represents a cell range containing RK value + * cells. All cells are located in the same row. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readMulRk(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $colFirst = self::getUInt2d($recordData, 2); + + // offset: var; size: 2; index to last column + $colLast = self::getUInt2d($recordData, $length - 2); + $columns = $colLast - $colFirst + 1; + + // offset within record data + $offset = 4; + + for ($i = 1; $i <= $columns; ++$i) { + $columnString = Coordinate::stringFromColumnIndex($colFirst + $i); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: var; size: 2; index to XF record + $xfIndex = self::getUInt2d($recordData, $offset); + + // offset: var; size: 4; RK value + $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2)); + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add style + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC); + } + + $offset += 6; + } + } + + /** + * Read NUMBER record + * This record represents a cell that contains a + * floating-point value. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readNumber(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size 2; index to column + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset 4; size: 2; index to XF record + $xfIndex = self::getUInt2d($recordData, 4); + + $numValue = self::extractNumber(substr($recordData, 6, 8)); + + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add cell style + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC); + } + } + + /** + * Read FORMULA record + perhaps a following STRING record if formula result is a string + * This record contains the token array and the result of a + * formula cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readFormula(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + // offset: 20: size: variable; formula structure + $formulaStructure = substr($recordData, 20); + + // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc. + $options = self::getUInt2d($recordData, 14); + + // bit: 0; mask: 0x0001; 1 = recalculate always + // bit: 1; mask: 0x0002; 1 = calculate on open + // bit: 2; mask: 0x0008; 1 = part of a shared formula + $isPartOfSharedFormula = (bool) (0x0008 & $options); + + // WARNING: + // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true + // the formula data may be ordinary formula data, therefore we need to check + // explicitly for the tExp token (0x01) + $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01; + + if ($isPartOfSharedFormula) { + // part of shared formula which means there will be a formula with a tExp token and nothing else + // get the base cell, grab tExp token + $baseRow = self::getUInt2d($formulaStructure, 3); + $baseCol = self::getUInt2d($formulaStructure, 5); + $this->baseCell = Coordinate::stringFromColumnIndex($baseCol + 1) . ($baseRow + 1); + } + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + if ($isPartOfSharedFormula) { + // formula is added to this cell after the sheet has been read + $this->sharedFormulaParts[$columnString . ($row + 1)] = $this->baseCell; + } + + // offset: 16: size: 4; not used + + // offset: 4; size: 2; XF index + $xfIndex = self::getUInt2d($recordData, 4); + + // offset: 6; size: 8; result of the formula + if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) { + // String formula. Result follows in appended STRING record + $dataType = DataType::TYPE_STRING; + + // read possible SHAREDFMLA record + $code = self::getUInt2d($this->data, $this->pos); + if ($code == self::XLS_TYPE_SHAREDFMLA) { + $this->readSharedFmla(); + } + + // read STRING record + $value = $this->readString(); + } elseif ( + (ord($recordData[6]) == 1) + && (ord($recordData[12]) == 255) + && (ord($recordData[13]) == 255) + ) { + // Boolean formula. Result is in +2; 0=false, 1=true + $dataType = DataType::TYPE_BOOL; + $value = (bool) ord($recordData[8]); + } elseif ( + (ord($recordData[6]) == 2) + && (ord($recordData[12]) == 255) + && (ord($recordData[13]) == 255) + ) { + // Error formula. Error code is in +2 + $dataType = DataType::TYPE_ERROR; + $value = Xls\ErrorCode::lookup(ord($recordData[8])); + } elseif ( + (ord($recordData[6]) == 3) + && (ord($recordData[12]) == 255) + && (ord($recordData[13]) == 255) + ) { + // Formula result is a null string + $dataType = DataType::TYPE_NULL; + $value = ''; + } else { + // forumla result is a number, first 14 bytes like _NUMBER record + $dataType = DataType::TYPE_NUMERIC; + $value = self::extractNumber(substr($recordData, 6, 8)); + } + + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add cell style + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + + // store the formula + if (!$isPartOfSharedFormula) { + // not part of shared formula + // add cell value. If we can read formula, populate with formula, otherwise just used cached value + try { + if ($this->version != self::XLS_BIFF8) { + throw new Exception('Not BIFF8. Can only read BIFF8 formulas'); + } + $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language + $cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA); + } catch (PhpSpreadsheetException $e) { + $cell->setValueExplicit($value, $dataType); + } + } else { + if ($this->version == self::XLS_BIFF8) { + // do nothing at this point, formula id added later in the code + } else { + $cell->setValueExplicit($value, $dataType); + } + } + + // store the cached calculated value + $cell->setCalculatedValue($value); + } + } + + /** + * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader, + * which usually contains relative references. + * These will be used to construct the formula in each shared formula part after the sheet is read. + */ + private function readSharedFmla(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything + $cellRange = substr($recordData, 0, 6); + $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax + + // offset: 6, size: 1; not used + + // offset: 7, size: 1; number of existing FORMULA records for this shared formula + $no = ord($recordData[7]); + + // offset: 8, size: var; Binary token array of the shared formula + $formula = substr($recordData, 8); + + // at this point we only store the shared formula for later use + $this->sharedFormulas[$this->baseCell] = $formula; + } + + /** + * Read a STRING record from current stream position and advance the stream pointer to next record + * This record is used for storing result from FORMULA record when it is a string, and + * it occurs directly after the FORMULA record. + * + * @return string The string contents as UTF-8 + */ + private function readString() + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringLong($recordData); + $value = $string['value']; + } else { + $string = $this->readByteStringLong($recordData); + $value = $string['value']; + } + + return $value; + } + + /** + * Read BOOLERR record + * This record represents a Boolean value or error value + * cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readBoolErr(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; column index + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: 4; size: 2; index to XF record + $xfIndex = self::getUInt2d($recordData, 4); + + // offset: 6; size: 1; the boolean value or error value + $boolErr = ord($recordData[6]); + + // offset: 7; size: 1; 0=boolean; 1=error + $isError = ord($recordData[7]); + + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + switch ($isError) { + case 0: // boolean + $value = (bool) $boolErr; + + // add cell value + $cell->setValueExplicit($value, DataType::TYPE_BOOL); + + break; + case 1: // error type + $value = Xls\ErrorCode::lookup($boolErr); + + // add cell value + $cell->setValueExplicit($value, DataType::TYPE_ERROR); + + break; + } + + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add cell style + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULBLANK record + * This record represents a cell range of empty cells. All + * cells are located in the same row. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readMulBlank(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $fc = self::getUInt2d($recordData, 2); + + // offset: 4; size: 2 x nc; list of indexes to XF records + // add style information + if (!$this->readDataOnly && $this->readEmptyCells) { + for ($i = 0; $i < $length / 2 - 3; ++$i) { + $columnString = Coordinate::stringFromColumnIndex($fc + $i + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + $xfIndex = self::getUInt2d($recordData, 4 + 2 * $i); + if (isset($this->mapCellXfIndex[$xfIndex])) { + $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + } + + // offset: 6; size 2; index to last column (not needed) + } + + /** + * Read LABEL record + * This record represents a cell that contains a string. In + * BIFF8 it is usually replaced by the LABELSST record. + * Excel still uses this record, if it copies unformatted + * text cells to the clipboard. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readLabel(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($column + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: 4; size: 2; XF index + $xfIndex = self::getUInt2d($recordData, 4); + + // add cell value + // todo: what if string is very long? continue record + if ($this->version == self::XLS_BIFF8) { + $string = self::readUnicodeStringLong(substr($recordData, 6)); + $value = $string['value']; + } else { + $string = $this->readByteStringLong(substr($recordData, 6)); + $value = $string['value']; + } + if ($this->readEmptyCells || trim($value) !== '') { + $cell = $this->phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($value, DataType::TYPE_STRING); + + if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) { + // add cell style + $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + } + + /** + * Read BLANK record. + */ + private function readBlank(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $col = self::getUInt2d($recordData, 2); + $columnString = Coordinate::stringFromColumnIndex($col + 1); + + // Read cell? + if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) { + // offset: 4; size: 2; XF index + $xfIndex = self::getUInt2d($recordData, 4); + + // add style information + if (!$this->readDataOnly && $this->readEmptyCells && isset($this->mapCellXfIndex[$xfIndex])) { + $this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MSODRAWING record. + */ + private function readMsoDrawing(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + + // get spliced record data + $splicedRecordData = $this->getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->drawingData .= $recordData; + } + + /** + * Read OBJ record. + */ + private function readObj(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly || $this->version != self::XLS_BIFF8) { + return; + } + + // recordData consists of an array of subrecords looking like this: + // ft: 2 bytes; ftCmo type (0x15) + // cb: 2 bytes; size in bytes of ftCmo data + // ot: 2 bytes; Object Type + // id: 2 bytes; Object id number + // grbit: 2 bytes; Option Flags + // data: var; subrecord data + + // for now, we are just interested in the second subrecord containing the object type + $ftCmoType = self::getUInt2d($recordData, 0); + $cbCmoSize = self::getUInt2d($recordData, 2); + $otObjType = self::getUInt2d($recordData, 4); + $idObjID = self::getUInt2d($recordData, 6); + $grbitOpts = self::getUInt2d($recordData, 6); + + $this->objs[] = [ + 'ftCmoType' => $ftCmoType, + 'cbCmoSize' => $cbCmoSize, + 'otObjType' => $otObjType, + 'idObjID' => $idObjID, + 'grbitOpts' => $grbitOpts, + ]; + $this->textObjRef = $idObjID; + } + + /** + * Read WINDOW2 record. + */ + private function readWindow2(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; option flags + $options = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; index to first visible row + $firstVisibleRow = self::getUInt2d($recordData, 2); + + // offset: 4; size: 2; index to first visible colum + $firstVisibleColumn = self::getUInt2d($recordData, 4); + if ($this->version === self::XLS_BIFF8) { + // offset: 8; size: 2; not used + // offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%) + // offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%) + // offset: 14; size: 4; not used + if (!isset($recordData[10])) { + $zoomscaleInPageBreakPreview = 0; + } else { + $zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10); + } + + if ($zoomscaleInPageBreakPreview === 0) { + $zoomscaleInPageBreakPreview = 60; + } + + if (!isset($recordData[12])) { + $zoomscaleInNormalView = 0; + } else { + $zoomscaleInNormalView = self::getUInt2d($recordData, 12); + } + + if ($zoomscaleInNormalView === 0) { + $zoomscaleInNormalView = 100; + } + } + + // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines + $showGridlines = (bool) ((0x0002 & $options) >> 1); + $this->phpSheet->setShowGridlines($showGridlines); + + // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers + $showRowColHeaders = (bool) ((0x0004 & $options) >> 2); + $this->phpSheet->setShowRowColHeaders($showRowColHeaders); + + // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen + $this->frozen = (bool) ((0x0008 & $options) >> 3); + + // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left + $this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6)); + + // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active + $isActive = (bool) ((0x0400 & $options) >> 10); + if ($isActive) { + $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet)); + } + + // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view + $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11); + + //FIXME: set $firstVisibleRow and $firstVisibleColumn + + if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) { + //NOTE: this setting is inferior to page layout view(Excel2007-) + $view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL; + $this->phpSheet->getSheetView()->setView($view); + if ($this->version === self::XLS_BIFF8) { + $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView; + $this->phpSheet->getSheetView()->setZoomScale($zoomScale); + $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView); + } + } + } + + /** + * Read PLV Record(Created by Excel2007 or upper). + */ + private function readPageLayoutView(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; rt + //->ignore + $rt = self::getUInt2d($recordData, 0); + // offset: 2; size: 2; grbitfr + //->ignore + $grbitFrt = self::getUInt2d($recordData, 2); + // offset: 4; size: 8; reserved + //->ignore + + // offset: 12; size 2; zoom scale + $wScalePLV = self::getUInt2d($recordData, 12); + // offset: 14; size 2; grbit + $grbit = self::getUInt2d($recordData, 14); + + // decomprise grbit + $fPageLayoutView = $grbit & 0x01; + $fRulerVisible = ($grbit >> 1) & 0x01; //no support + $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support + + if ($fPageLayoutView === 1) { + $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT); + $this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT + } + //otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW. + } + + /** + * Read SCL record. + */ + private function readScl(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // offset: 0; size: 2; numerator of the view magnification + $numerator = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; numerator of the view magnification + $denumerator = self::getUInt2d($recordData, 2); + + // set the zoom scale (in percent) + $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator); + } + + /** + * Read PANE record. + */ + private function readPane(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; position of vertical split + $px = self::getUInt2d($recordData, 0); + + // offset: 2; size: 2; position of horizontal split + $py = self::getUInt2d($recordData, 2); + + // offset: 4; size: 2; top most visible row in the bottom pane + $rwTop = self::getUInt2d($recordData, 4); + + // offset: 6; size: 2; first visible left column in the right pane + $colLeft = self::getUInt2d($recordData, 6); + + if ($this->frozen) { + // frozen panes + $cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1); + $topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1); + $this->phpSheet->freezePane($cell, $topLeftCell); + } + // unfrozen panes; split windows; not supported by PhpSpreadsheet core + } + } + + /** + * Read SELECTION record. There is one such record for each pane in the sheet. + */ + private function readSelection(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 1; pane identifier + $paneId = ord($recordData[0]); + + // offset: 1; size: 2; index to row of the active cell + $r = self::getUInt2d($recordData, 1); + + // offset: 3; size: 2; index to column of the active cell + $c = self::getUInt2d($recordData, 3); + + // offset: 5; size: 2; index into the following cell range list to the + // entry that contains the active cell + $index = self::getUInt2d($recordData, 5); + + // offset: 7; size: var; cell range address list containing all selected cell ranges + $data = substr($recordData, 7); + $cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax + + $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0]; + + // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) + if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); + } + + // first row '1' + last row '65536' indicates that full column is selected + if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); + } + + // first column 'A' + last column 'IV' indicates that full row is selected + if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) { + $selectedCells = preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells); + } + + $this->phpSheet->setSelectedCells($selectedCells); + } + } + + private function includeCellRangeFiltered($cellRangeAddress) + { + $includeCellRange = true; + if ($this->getReadFilter() !== null) { + $includeCellRange = false; + $rangeBoundaries = Coordinate::getRangeBoundaries($cellRangeAddress); + ++$rangeBoundaries[1][0]; + for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) { + for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) { + if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) { + $includeCellRange = true; + + break 2; + } + } + } + } + + return $includeCellRange; + } + + /** + * MERGEDCELLS. + * + * This record contains the addresses of merged cell ranges + * in the current sheet. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function readMergedCells(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) { + $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData); + foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) { + if ( + (strpos($cellRangeAddress, ':') !== false) && + ($this->includeCellRangeFiltered($cellRangeAddress)) + ) { + $this->phpSheet->mergeCells($cellRangeAddress); + } + } + } + } + + /** + * Read HYPERLINK record. + */ + private function readHyperLink(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer forward to next record + $this->pos += 4 + $length; + + if (!$this->readDataOnly) { + // offset: 0; size: 8; cell range address of all cells containing this hyperlink + try { + $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData); + } catch (PhpSpreadsheetException $e) { + return; + } + + // offset: 8, size: 16; GUID of StdLink + + // offset: 24, size: 4; unknown value + + // offset: 28, size: 4; option flags + // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL + $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0; + + // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL + $isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1; + + // bit: 2 (and 4); mask: 0x00000014; 0 = no description + $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2; + + // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text + $hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3; + + // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame + $hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7; + + // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name) + $isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8; + + // offset within record data + $offset = 32; + + if ($hasDesc) { + // offset: 32; size: var; character count of description text + $dl = self::getInt4d($recordData, 32); + // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated + $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false); + $offset += 4 + 2 * $dl; + } + if ($hasFrame) { + $fl = self::getInt4d($recordData, $offset); + $offset += 4 + 2 * $fl; + } + + // detect type of hyperlink (there are 4 types) + $hyperlinkType = null; + + if ($isUNC) { + $hyperlinkType = 'UNC'; + } elseif (!$isFileLinkOrUrl) { + $hyperlinkType = 'workbook'; + } elseif (ord($recordData[$offset]) == 0x03) { + $hyperlinkType = 'local'; + } elseif (ord($recordData[$offset]) == 0xE0) { + $hyperlinkType = 'URL'; + } + + switch ($hyperlinkType) { + case 'URL': + // section 5.58.2: Hyperlink containing a URL + // e.g. http://example.org/index.php + + // offset: var; size: 16; GUID of URL Moniker + $offset += 16; + // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word + $us = self::getInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated + $url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false); + $nullOffset = strpos($url, chr(0x00)); + if ($nullOffset) { + $url = substr($url, 0, $nullOffset); + } + $url .= $hasText ? '#' : ''; + $offset += $us; + + break; + case 'local': + // section 5.58.3: Hyperlink to local file + // examples: + // mydoc.txt + // ../../somedoc.xls#Sheet!A1 + + // offset: var; size: 16; GUI of File Moniker + $offset += 16; + + // offset: var; size: 2; directory up-level count. + $upLevelCount = self::getUInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word + $sl = self::getInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string) + $shortenedFilePath = substr($recordData, $offset, $sl); + $shortenedFilePath = self::encodeUTF16($shortenedFilePath, true); + $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero + + $offset += $sl; + + // offset: var; size: 24; unknown sequence + $offset += 24; + + // extended file path + // offset: var; size: 4; size of the following file link field including string lenth mark + $sz = self::getInt4d($recordData, $offset); + $offset += 4; + + // only present if $sz > 0 + if ($sz > 0) { + // offset: var; size: 4; size of the character array of the extended file path and name + $xl = self::getInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size 2; unknown + $offset += 2; + + // offset: var; size $xl; character array of the extended file path and name. + $extendedFilePath = substr($recordData, $offset, $xl); + $extendedFilePath = self::encodeUTF16($extendedFilePath, false); + $offset += $xl; + } + + // construct the path + $url = str_repeat('..\\', $upLevelCount); + $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available + $url .= $hasText ? '#' : ''; + + break; + case 'UNC': + // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path + // todo: implement + return; + case 'workbook': + // section 5.58.5: Hyperlink to the Current Workbook + // e.g. Sheet2!B1:C2, stored in text mark field + $url = 'sheet://'; + + break; + default: + return; + } + + if ($hasText) { + // offset: var; size: 4; character count of text mark including trailing zero word + $tl = self::getInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated + $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false); + $url .= $text; + } + + // apply the hyperlink to all the relevant cells + foreach (Coordinate::extractAllCellReferencesInRange($cellRange) as $coordinate) { + $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url); + } + } + } + + /** + * Read DATAVALIDATIONS record. + */ + private function readDataValidations(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer forward to next record + $this->pos += 4 + $length; + } + + /** + * Read DATAVALIDATION record. + */ + private function readDataValidation(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer forward to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // offset: 0; size: 4; Options + $options = self::getInt4d($recordData, 0); + + // bit: 0-3; mask: 0x0000000F; type + $type = (0x0000000F & $options) >> 0; + switch ($type) { + case 0x00: + $type = DataValidation::TYPE_NONE; + + break; + case 0x01: + $type = DataValidation::TYPE_WHOLE; + + break; + case 0x02: + $type = DataValidation::TYPE_DECIMAL; + + break; + case 0x03: + $type = DataValidation::TYPE_LIST; + + break; + case 0x04: + $type = DataValidation::TYPE_DATE; + + break; + case 0x05: + $type = DataValidation::TYPE_TIME; + + break; + case 0x06: + $type = DataValidation::TYPE_TEXTLENGTH; + + break; + case 0x07: + $type = DataValidation::TYPE_CUSTOM; + + break; + } + + // bit: 4-6; mask: 0x00000070; error type + $errorStyle = (0x00000070 & $options) >> 4; + switch ($errorStyle) { + case 0x00: + $errorStyle = DataValidation::STYLE_STOP; + + break; + case 0x01: + $errorStyle = DataValidation::STYLE_WARNING; + + break; + case 0x02: + $errorStyle = DataValidation::STYLE_INFORMATION; + + break; + } + + // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) + // I have only seen cases where this is 1 + $explicitFormula = (0x00000080 & $options) >> 7; + + // bit: 8; mask: 0x00000100; 1= empty cells allowed + $allowBlank = (0x00000100 & $options) >> 8; + + // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity + $suppressDropDown = (0x00000200 & $options) >> 9; + + // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected + $showInputMessage = (0x00040000 & $options) >> 18; + + // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered + $showErrorMessage = (0x00080000 & $options) >> 19; + + // bit: 20-23; mask: 0x00F00000; condition operator + $operator = (0x00F00000 & $options) >> 20; + switch ($operator) { + case 0x00: + $operator = DataValidation::OPERATOR_BETWEEN; + + break; + case 0x01: + $operator = DataValidation::OPERATOR_NOTBETWEEN; + + break; + case 0x02: + $operator = DataValidation::OPERATOR_EQUAL; + + break; + case 0x03: + $operator = DataValidation::OPERATOR_NOTEQUAL; + + break; + case 0x04: + $operator = DataValidation::OPERATOR_GREATERTHAN; + + break; + case 0x05: + $operator = DataValidation::OPERATOR_LESSTHAN; + + break; + case 0x06: + $operator = DataValidation::OPERATOR_GREATERTHANOREQUAL; + + break; + case 0x07: + $operator = DataValidation::OPERATOR_LESSTHANOREQUAL; + + break; + } + + // offset: 4; size: var; title of the prompt box + $offset = 4; + $string = self::readUnicodeStringLong(substr($recordData, $offset)); + $promptTitle = $string['value'] !== chr(0) ? $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; title of the error box + $string = self::readUnicodeStringLong(substr($recordData, $offset)); + $errorTitle = $string['value'] !== chr(0) ? $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the prompt box + $string = self::readUnicodeStringLong(substr($recordData, $offset)); + $prompt = $string['value'] !== chr(0) ? $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the error box + $string = self::readUnicodeStringLong(substr($recordData, $offset)); + $error = $string['value'] !== chr(0) ? $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: 2; size of the formula data for the first condition + $sz1 = self::getUInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz1; formula data for first condition (without size field) + $formula1 = substr($recordData, $offset, $sz1); + $formula1 = pack('v', $sz1) . $formula1; // prepend the length + + try { + $formula1 = $this->getFormulaFromStructure($formula1); + + // in list type validity, null characters are used as item separators + if ($type == DataValidation::TYPE_LIST) { + $formula1 = str_replace(chr(0), ',', $formula1); + } + } catch (PhpSpreadsheetException $e) { + return; + } + $offset += $sz1; + + // offset: var; size: 2; size of the formula data for the first condition + $sz2 = self::getUInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz2; formula data for second condition (without size field) + $formula2 = substr($recordData, $offset, $sz2); + $formula2 = pack('v', $sz2) . $formula2; // prepend the length + + try { + $formula2 = $this->getFormulaFromStructure($formula2); + } catch (PhpSpreadsheetException $e) { + return; + } + $offset += $sz2; + + // offset: var; size: var; cell range address list with + $cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset)); + $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; + + foreach ($cellRangeAddresses as $cellRange) { + $stRange = $this->phpSheet->shrinkRangeToFit($cellRange); + foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $coordinate) { + $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation(); + $objValidation->setType($type); + $objValidation->setErrorStyle($errorStyle); + $objValidation->setAllowBlank((bool) $allowBlank); + $objValidation->setShowInputMessage((bool) $showInputMessage); + $objValidation->setShowErrorMessage((bool) $showErrorMessage); + $objValidation->setShowDropDown(!$suppressDropDown); + $objValidation->setOperator($operator); + $objValidation->setErrorTitle($errorTitle); + $objValidation->setError($error); + $objValidation->setPromptTitle($promptTitle); + $objValidation->setPrompt($prompt); + $objValidation->setFormula1($formula1); + $objValidation->setFormula2($formula2); + } + } + } + + /** + * Read SHEETLAYOUT record. Stores sheet tab color information. + */ + private function readSheetLayout(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->readDataOnly) { + // offset: 0; size: 2; repeated record identifier 0x0862 + + // offset: 2; size: 10; not used + + // offset: 12; size: 4; size of record data + // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?) + $sz = self::getInt4d($recordData, 12); + + switch ($sz) { + case 0x14: + // offset: 16; size: 2; color index for sheet tab + $colorIndex = self::getUInt2d($recordData, 16); + $color = Xls\Color::map($colorIndex, $this->palette, $this->version); + $this->phpSheet->getTabColor()->setRGB($color['rgb']); + + break; + case 0x28: + // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007 + return; + + break; + } + } + } + + /** + * Read SHEETPROTECTION record (FEATHEADR). + */ + private function readSheetProtection(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + if ($this->readDataOnly) { + return; + } + + // offset: 0; size: 2; repeated record header + + // offset: 2; size: 2; FRT cell reference flag (=0 currently) + + // offset: 4; size: 8; Currently not used and set to 0 + + // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag) + $isf = self::getUInt2d($recordData, 12); + if ($isf != 2) { + return; + } + + // offset: 14; size: 1; =1 since this is a feat header + + // offset: 15; size: 4; size of rgbHdrSData + + // rgbHdrSData, assume "Enhanced Protection" + // offset: 19; size: 2; option flags + $options = self::getUInt2d($recordData, 19); + + // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects + $bool = (0x0001 & $options) >> 0; + $this->phpSheet->getProtection()->setObjects(!$bool); + + // bit: 1; mask 0x0002; edit scenarios + $bool = (0x0002 & $options) >> 1; + $this->phpSheet->getProtection()->setScenarios(!$bool); + + // bit: 2; mask 0x0004; format cells + $bool = (0x0004 & $options) >> 2; + $this->phpSheet->getProtection()->setFormatCells(!$bool); + + // bit: 3; mask 0x0008; format columns + $bool = (0x0008 & $options) >> 3; + $this->phpSheet->getProtection()->setFormatColumns(!$bool); + + // bit: 4; mask 0x0010; format rows + $bool = (0x0010 & $options) >> 4; + $this->phpSheet->getProtection()->setFormatRows(!$bool); + + // bit: 5; mask 0x0020; insert columns + $bool = (0x0020 & $options) >> 5; + $this->phpSheet->getProtection()->setInsertColumns(!$bool); + + // bit: 6; mask 0x0040; insert rows + $bool = (0x0040 & $options) >> 6; + $this->phpSheet->getProtection()->setInsertRows(!$bool); + + // bit: 7; mask 0x0080; insert hyperlinks + $bool = (0x0080 & $options) >> 7; + $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool); + + // bit: 8; mask 0x0100; delete columns + $bool = (0x0100 & $options) >> 8; + $this->phpSheet->getProtection()->setDeleteColumns(!$bool); + + // bit: 9; mask 0x0200; delete rows + $bool = (0x0200 & $options) >> 9; + $this->phpSheet->getProtection()->setDeleteRows(!$bool); + + // bit: 10; mask 0x0400; select locked cells + $bool = (0x0400 & $options) >> 10; + $this->phpSheet->getProtection()->setSelectLockedCells(!$bool); + + // bit: 11; mask 0x0800; sort cell range + $bool = (0x0800 & $options) >> 11; + $this->phpSheet->getProtection()->setSort(!$bool); + + // bit: 12; mask 0x1000; auto filter + $bool = (0x1000 & $options) >> 12; + $this->phpSheet->getProtection()->setAutoFilter(!$bool); + + // bit: 13; mask 0x2000; pivot tables + $bool = (0x2000 & $options) >> 13; + $this->phpSheet->getProtection()->setPivotTables(!$bool); + + // bit: 14; mask 0x4000; select unlocked cells + $bool = (0x4000 & $options) >> 14; + $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool); + + // offset: 21; size: 2; not used + } + + /** + * Read RANGEPROTECTION record + * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification, + * where it is referred to as FEAT record. + */ + private function readRangeProtection(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // move stream pointer to next record + $this->pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->readDataOnly) { + $offset += 12; + + // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag + $isf = self::getUInt2d($recordData, 12); + if ($isf != 2) { + // we only read FEAT records of type 2 + return; + } + $offset += 2; + + $offset += 5; + + // offset: 19; size: 2; count of ref ranges this feature is on + $cref = self::getUInt2d($recordData, 19); + $offset += 2; + + $offset += 6; + + // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record) + $cellRanges = []; + for ($i = 0; $i < $cref; ++$i) { + try { + $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8)); + } catch (PhpSpreadsheetException $e) { + return; + } + $cellRanges[] = $cellRange; + $offset += 8; + } + + // offset: var; size: var; variable length of feature specific data + $rgbFeat = substr($recordData, $offset); + $offset += 4; + + // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit) + $wPassword = self::getInt4d($recordData, $offset); + $offset += 4; + + // Apply range protection to sheet + if ($cellRanges) { + $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true); + } + } + } + + /** + * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record + * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented. + * In this case, we must treat the CONTINUE record as a MSODRAWING record. + */ + private function readContinue(): void + { + $length = self::getUInt2d($this->data, $this->pos + 2); + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); + + // check if we are reading drawing data + // this is in case a free CONTINUE record occurs in other circumstances we are unaware of + if ($this->drawingData == '') { + // move stream pointer to next record + $this->pos += 4 + $length; + + return; + } + + // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data + if ($length < 4) { + // move stream pointer to next record + $this->pos += 4 + $length; + + return; + } + + // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record + // look inside CONTINUE record to see if it looks like a part of an Escher stream + // we know that Escher stream may be split at least at + // 0xF003 MsofbtSpgrContainer + // 0xF004 MsofbtSpContainer + // 0xF00D MsofbtClientTextbox + $validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more + + $splitPoint = self::getUInt2d($recordData, 2); + if (in_array($splitPoint, $validSplitPoints)) { + // get spliced record data (and move pointer to next record) + $splicedRecordData = $this->getSplicedRecordData(); + $this->drawingData .= $splicedRecordData['recordData']; + + return; + } + + // move stream pointer to next record + $this->pos += 4 + $length; + } + + /** + * Reads a record from current position in data stream and continues reading data as long as CONTINUE + * records are found. Splices the record data pieces and returns the combined string as if record data + * is in one piece. + * Moves to next current position in data stream to start of next record different from a CONtINUE record. + * + * @return array + */ + private function getSplicedRecordData() + { + $data = ''; + $spliceOffsets = []; + + $i = 0; + $spliceOffsets[0] = 0; + + do { + ++$i; + + // offset: 0; size: 2; identifier + $identifier = self::getUInt2d($this->data, $this->pos); + // offset: 2; size: 2; length + $length = self::getUInt2d($this->data, $this->pos + 2); + $data .= $this->readRecordData($this->data, $this->pos + 4, $length); + + $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; + + $this->pos += 4 + $length; + $nextIdentifier = self::getUInt2d($this->data, $this->pos); + } while ($nextIdentifier == self::XLS_TYPE_CONTINUE); + + return [ + 'recordData' => $data, + 'spliceOffsets' => $spliceOffsets, + ]; + } + + /** + * Convert formula structure into human readable Excel formula like 'A3+A5*5'. + * + * @param string $formulaStructure The complete binary data for the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * + * @return string Human readable formula + */ + private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1') + { + // offset: 0; size: 2; size of the following formula data + $sz = self::getUInt2d($formulaStructure, 0); + + // offset: 2; size: sz + $formulaData = substr($formulaStructure, 2, $sz); + + // offset: 2 + sz; size: variable (optional) + if (strlen($formulaStructure) > 2 + $sz) { + $additionalData = substr($formulaStructure, 2 + $sz); + } else { + $additionalData = ''; + } + + return $this->getFormulaFromData($formulaData, $additionalData, $baseCell); + } + + /** + * Take formula data and additional data for formula and return human readable formula. + * + * @param string $formulaData The binary data for the formula itself + * @param string $additionalData Additional binary data going with the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * + * @return string Human readable formula + */ + private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1') + { + // start parsing the formula data + $tokens = []; + + while (strlen($formulaData) > 0 && $token = $this->getNextToken($formulaData, $baseCell)) { + $tokens[] = $token; + $formulaData = substr($formulaData, $token['size']); + } + + $formulaString = $this->createFormulaFromTokens($tokens, $additionalData); + + return $formulaString; + } + + /** + * Take array of tokens together with additional data for formula and return human readable formula. + * + * @param array $tokens + * @param string $additionalData Additional binary data going with the formula + * + * @return string Human readable formula + */ + private function createFormulaFromTokens($tokens, $additionalData) + { + // empty formula? + if (empty($tokens)) { + return ''; + } + + $formulaStrings = []; + foreach ($tokens as $token) { + // initialize spaces + $space0 = $space0 ?? ''; // spaces before next token, not tParen + $space1 = $space1 ?? ''; // carriage returns before next token, not tParen + $space2 = $space2 ?? ''; // spaces before opening parenthesis + $space3 = $space3 ?? ''; // carriage returns before opening parenthesis + $space4 = $space4 ?? ''; // spaces before closing parenthesis + $space5 = $space5 ?? ''; // carriage returns before closing parenthesis + + switch ($token['name']) { + case 'tAdd': // addition + case 'tConcat': // addition + case 'tDiv': // division + case 'tEQ': // equality + case 'tGE': // greater than or equal + case 'tGT': // greater than + case 'tIsect': // intersection + case 'tLE': // less than or equal + case 'tList': // less than or equal + case 'tLT': // less than + case 'tMul': // multiplication + case 'tNE': // multiplication + case 'tPower': // power + case 'tRange': // range + case 'tSub': // subtraction + $op2 = array_pop($formulaStrings); + $op1 = array_pop($formulaStrings); + $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2"; + unset($space0, $space1); + + break; + case 'tUplus': // unary plus + case 'tUminus': // unary minus + $op = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0{$token['data']}$op"; + unset($space0, $space1); + + break; + case 'tPercent': // percent sign + $op = array_pop($formulaStrings); + $formulaStrings[] = "$op$space1$space0{$token['data']}"; + unset($space0, $space1); + + break; + case 'tAttrVolatile': // indicates volatile function + case 'tAttrIf': + case 'tAttrSkip': + case 'tAttrChoose': + // token is only important for Excel formula evaluator + // do nothing + break; + case 'tAttrSpace': // space / carriage return + // space will be used when next token arrives, do not alter formulaString stack + switch ($token['data']['spacetype']) { + case 'type0': + $space0 = str_repeat(' ', $token['data']['spacecount']); + + break; + case 'type1': + $space1 = str_repeat("\n", $token['data']['spacecount']); + + break; + case 'type2': + $space2 = str_repeat(' ', $token['data']['spacecount']); + + break; + case 'type3': + $space3 = str_repeat("\n", $token['data']['spacecount']); + + break; + case 'type4': + $space4 = str_repeat(' ', $token['data']['spacecount']); + + break; + case 'type5': + $space5 = str_repeat("\n", $token['data']['spacecount']); + + break; + } + + break; + case 'tAttrSum': // SUM function with one parameter + $op = array_pop($formulaStrings); + $formulaStrings[] = "{$space1}{$space0}SUM($op)"; + unset($space0, $space1); + + break; + case 'tFunc': // function with fixed number of arguments + case 'tFuncV': // function with variable number of arguments + if ($token['data']['function'] != '') { + // normal function + $ops = []; // array of operators + for ($i = 0; $i < $token['data']['args']; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')'; + unset($space0, $space1); + } else { + // add-in function + $ops = []; // array of operators + for ($i = 0; $i < $token['data']['args'] - 1; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $function = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')'; + unset($space0, $space1); + } + + break; + case 'tParen': // parenthesis + $expression = array_pop($formulaStrings); + $formulaStrings[] = "$space3$space2($expression$space5$space4)"; + unset($space2, $space3, $space4, $space5); + + break; + case 'tArray': // array constant + $constantArray = self::readBIFF8ConstantArray($additionalData); + $formulaStrings[] = $space1 . $space0 . $constantArray['value']; + $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data + unset($space0, $space1); + + break; + case 'tMemArea': + // bite off chunk of additional data + $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData); + $additionalData = substr($additionalData, $cellRangeAddressList['size']); + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + + break; + case 'tArea': // cell range address + case 'tBool': // boolean + case 'tErr': // error code + case 'tInt': // integer + case 'tMemErr': + case 'tMemFunc': + case 'tMissArg': + case 'tName': + case 'tNameX': + case 'tNum': // number + case 'tRef': // single cell reference + case 'tRef3d': // 3d cell reference + case 'tArea3d': // 3d cell range reference + case 'tRefN': + case 'tAreaN': + case 'tStr': // string + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + + break; + } + } + $formulaString = $formulaStrings[0]; + + return $formulaString; + } + + /** + * Fetch next token from binary formula data. + * + * @param string $formulaData Formula data + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * + * @return array + */ + private function getNextToken($formulaData, $baseCell = 'A1') + { + // offset: 0; size: 1; token id + $id = ord($formulaData[0]); // token id + $name = false; // initialize token name + + switch ($id) { + case 0x03: + $name = 'tAdd'; + $size = 1; + $data = '+'; + + break; + case 0x04: + $name = 'tSub'; + $size = 1; + $data = '-'; + + break; + case 0x05: + $name = 'tMul'; + $size = 1; + $data = '*'; + + break; + case 0x06: + $name = 'tDiv'; + $size = 1; + $data = '/'; + + break; + case 0x07: + $name = 'tPower'; + $size = 1; + $data = '^'; + + break; + case 0x08: + $name = 'tConcat'; + $size = 1; + $data = '&'; + + break; + case 0x09: + $name = 'tLT'; + $size = 1; + $data = '<'; + + break; + case 0x0A: + $name = 'tLE'; + $size = 1; + $data = '<='; + + break; + case 0x0B: + $name = 'tEQ'; + $size = 1; + $data = '='; + + break; + case 0x0C: + $name = 'tGE'; + $size = 1; + $data = '>='; + + break; + case 0x0D: + $name = 'tGT'; + $size = 1; + $data = '>'; + + break; + case 0x0E: + $name = 'tNE'; + $size = 1; + $data = '<>'; + + break; + case 0x0F: + $name = 'tIsect'; + $size = 1; + $data = ' '; + + break; + case 0x10: + $name = 'tList'; + $size = 1; + $data = ','; + + break; + case 0x11: + $name = 'tRange'; + $size = 1; + $data = ':'; + + break; + case 0x12: + $name = 'tUplus'; + $size = 1; + $data = '+'; + + break; + case 0x13: + $name = 'tUminus'; + $size = 1; + $data = '-'; + + break; + case 0x14: + $name = 'tPercent'; + $size = 1; + $data = '%'; + + break; + case 0x15: // parenthesis + $name = 'tParen'; + $size = 1; + $data = null; + + break; + case 0x16: // missing argument + $name = 'tMissArg'; + $size = 1; + $data = ''; + + break; + case 0x17: // string + $name = 'tStr'; + // offset: 1; size: var; Unicode string, 8-bit string length + $string = self::readUnicodeStringShort(substr($formulaData, 1)); + $size = 1 + $string['size']; + $data = self::UTF8toExcelDoubleQuoted($string['value']); + + break; + case 0x19: // Special attribute + // offset: 1; size: 1; attribute type flags: + switch (ord($formulaData[1])) { + case 0x01: + $name = 'tAttrVolatile'; + $size = 4; + $data = null; + + break; + case 0x02: + $name = 'tAttrIf'; + $size = 4; + $data = null; + + break; + case 0x04: + $name = 'tAttrChoose'; + // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1) + $nc = self::getUInt2d($formulaData, 2); + // offset: 4; size: 2 * $nc + // offset: 4 + 2 * $nc; size: 2 + $size = 2 * $nc + 6; + $data = null; + + break; + case 0x08: + $name = 'tAttrSkip'; + $size = 4; + $data = null; + + break; + case 0x10: + $name = 'tAttrSum'; + $size = 4; + $data = null; + + break; + case 0x40: + case 0x41: + $name = 'tAttrSpace'; + $size = 4; + // offset: 2; size: 2; space type and position + switch (ord($formulaData[2])) { + case 0x00: + $spacetype = 'type0'; + + break; + case 0x01: + $spacetype = 'type1'; + + break; + case 0x02: + $spacetype = 'type2'; + + break; + case 0x03: + $spacetype = 'type3'; + + break; + case 0x04: + $spacetype = 'type4'; + + break; + case 0x05: + $spacetype = 'type5'; + + break; + default: + throw new Exception('Unrecognized space type in tAttrSpace token'); + + break; + } + // offset: 3; size: 1; number of inserted spaces/carriage returns + $spacecount = ord($formulaData[3]); + + $data = ['spacetype' => $spacetype, 'spacecount' => $spacecount]; + + break; + default: + throw new Exception('Unrecognized attribute flag in tAttr token'); + + break; + } + + break; + case 0x1C: // error code + // offset: 1; size: 1; error code + $name = 'tErr'; + $size = 2; + $data = Xls\ErrorCode::lookup(ord($formulaData[1])); + + break; + case 0x1D: // boolean + // offset: 1; size: 1; 0 = false, 1 = true; + $name = 'tBool'; + $size = 2; + $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE'; + + break; + case 0x1E: // integer + // offset: 1; size: 2; unsigned 16-bit integer + $name = 'tInt'; + $size = 3; + $data = self::getUInt2d($formulaData, 1); + + break; + case 0x1F: // number + // offset: 1; size: 8; + $name = 'tNum'; + $size = 9; + $data = self::extractNumber(substr($formulaData, 1)); + $data = str_replace(',', '.', (string) $data); // in case non-English locale + + break; + case 0x20: // array constant + case 0x40: + case 0x60: + // offset: 1; size: 7; not used + $name = 'tArray'; + $size = 8; + $data = null; + + break; + case 0x21: // function with fixed number of arguments + case 0x41: + case 0x61: + $name = 'tFunc'; + $size = 3; + // offset: 1; size: 2; index to built-in sheet function + switch (self::getUInt2d($formulaData, 1)) { + case 2: + $function = 'ISNA'; + $args = 1; + + break; + case 3: + $function = 'ISERROR'; + $args = 1; + + break; + case 10: + $function = 'NA'; + $args = 0; + + break; + case 15: + $function = 'SIN'; + $args = 1; + + break; + case 16: + $function = 'COS'; + $args = 1; + + break; + case 17: + $function = 'TAN'; + $args = 1; + + break; + case 18: + $function = 'ATAN'; + $args = 1; + + break; + case 19: + $function = 'PI'; + $args = 0; + + break; + case 20: + $function = 'SQRT'; + $args = 1; + + break; + case 21: + $function = 'EXP'; + $args = 1; + + break; + case 22: + $function = 'LN'; + $args = 1; + + break; + case 23: + $function = 'LOG10'; + $args = 1; + + break; + case 24: + $function = 'ABS'; + $args = 1; + + break; + case 25: + $function = 'INT'; + $args = 1; + + break; + case 26: + $function = 'SIGN'; + $args = 1; + + break; + case 27: + $function = 'ROUND'; + $args = 2; + + break; + case 30: + $function = 'REPT'; + $args = 2; + + break; + case 31: + $function = 'MID'; + $args = 3; + + break; + case 32: + $function = 'LEN'; + $args = 1; + + break; + case 33: + $function = 'VALUE'; + $args = 1; + + break; + case 34: + $function = 'TRUE'; + $args = 0; + + break; + case 35: + $function = 'FALSE'; + $args = 0; + + break; + case 38: + $function = 'NOT'; + $args = 1; + + break; + case 39: + $function = 'MOD'; + $args = 2; + + break; + case 40: + $function = 'DCOUNT'; + $args = 3; + + break; + case 41: + $function = 'DSUM'; + $args = 3; + + break; + case 42: + $function = 'DAVERAGE'; + $args = 3; + + break; + case 43: + $function = 'DMIN'; + $args = 3; + + break; + case 44: + $function = 'DMAX'; + $args = 3; + + break; + case 45: + $function = 'DSTDEV'; + $args = 3; + + break; + case 48: + $function = 'TEXT'; + $args = 2; + + break; + case 61: + $function = 'MIRR'; + $args = 3; + + break; + case 63: + $function = 'RAND'; + $args = 0; + + break; + case 65: + $function = 'DATE'; + $args = 3; + + break; + case 66: + $function = 'TIME'; + $args = 3; + + break; + case 67: + $function = 'DAY'; + $args = 1; + + break; + case 68: + $function = 'MONTH'; + $args = 1; + + break; + case 69: + $function = 'YEAR'; + $args = 1; + + break; + case 71: + $function = 'HOUR'; + $args = 1; + + break; + case 72: + $function = 'MINUTE'; + $args = 1; + + break; + case 73: + $function = 'SECOND'; + $args = 1; + + break; + case 74: + $function = 'NOW'; + $args = 0; + + break; + case 75: + $function = 'AREAS'; + $args = 1; + + break; + case 76: + $function = 'ROWS'; + $args = 1; + + break; + case 77: + $function = 'COLUMNS'; + $args = 1; + + break; + case 83: + $function = 'TRANSPOSE'; + $args = 1; + + break; + case 86: + $function = 'TYPE'; + $args = 1; + + break; + case 97: + $function = 'ATAN2'; + $args = 2; + + break; + case 98: + $function = 'ASIN'; + $args = 1; + + break; + case 99: + $function = 'ACOS'; + $args = 1; + + break; + case 105: + $function = 'ISREF'; + $args = 1; + + break; + case 111: + $function = 'CHAR'; + $args = 1; + + break; + case 112: + $function = 'LOWER'; + $args = 1; + + break; + case 113: + $function = 'UPPER'; + $args = 1; + + break; + case 114: + $function = 'PROPER'; + $args = 1; + + break; + case 117: + $function = 'EXACT'; + $args = 2; + + break; + case 118: + $function = 'TRIM'; + $args = 1; + + break; + case 119: + $function = 'REPLACE'; + $args = 4; + + break; + case 121: + $function = 'CODE'; + $args = 1; + + break; + case 126: + $function = 'ISERR'; + $args = 1; + + break; + case 127: + $function = 'ISTEXT'; + $args = 1; + + break; + case 128: + $function = 'ISNUMBER'; + $args = 1; + + break; + case 129: + $function = 'ISBLANK'; + $args = 1; + + break; + case 130: + $function = 'T'; + $args = 1; + + break; + case 131: + $function = 'N'; + $args = 1; + + break; + case 140: + $function = 'DATEVALUE'; + $args = 1; + + break; + case 141: + $function = 'TIMEVALUE'; + $args = 1; + + break; + case 142: + $function = 'SLN'; + $args = 3; + + break; + case 143: + $function = 'SYD'; + $args = 4; + + break; + case 162: + $function = 'CLEAN'; + $args = 1; + + break; + case 163: + $function = 'MDETERM'; + $args = 1; + + break; + case 164: + $function = 'MINVERSE'; + $args = 1; + + break; + case 165: + $function = 'MMULT'; + $args = 2; + + break; + case 184: + $function = 'FACT'; + $args = 1; + + break; + case 189: + $function = 'DPRODUCT'; + $args = 3; + + break; + case 190: + $function = 'ISNONTEXT'; + $args = 1; + + break; + case 195: + $function = 'DSTDEVP'; + $args = 3; + + break; + case 196: + $function = 'DVARP'; + $args = 3; + + break; + case 198: + $function = 'ISLOGICAL'; + $args = 1; + + break; + case 199: + $function = 'DCOUNTA'; + $args = 3; + + break; + case 207: + $function = 'REPLACEB'; + $args = 4; + + break; + case 210: + $function = 'MIDB'; + $args = 3; + + break; + case 211: + $function = 'LENB'; + $args = 1; + + break; + case 212: + $function = 'ROUNDUP'; + $args = 2; + + break; + case 213: + $function = 'ROUNDDOWN'; + $args = 2; + + break; + case 214: + $function = 'ASC'; + $args = 1; + + break; + case 215: + $function = 'DBCS'; + $args = 1; + + break; + case 221: + $function = 'TODAY'; + $args = 0; + + break; + case 229: + $function = 'SINH'; + $args = 1; + + break; + case 230: + $function = 'COSH'; + $args = 1; + + break; + case 231: + $function = 'TANH'; + $args = 1; + + break; + case 232: + $function = 'ASINH'; + $args = 1; + + break; + case 233: + $function = 'ACOSH'; + $args = 1; + + break; + case 234: + $function = 'ATANH'; + $args = 1; + + break; + case 235: + $function = 'DGET'; + $args = 3; + + break; + case 244: + $function = 'INFO'; + $args = 1; + + break; + case 252: + $function = 'FREQUENCY'; + $args = 2; + + break; + case 261: + $function = 'ERROR.TYPE'; + $args = 1; + + break; + case 271: + $function = 'GAMMALN'; + $args = 1; + + break; + case 273: + $function = 'BINOMDIST'; + $args = 4; + + break; + case 274: + $function = 'CHIDIST'; + $args = 2; + + break; + case 275: + $function = 'CHIINV'; + $args = 2; + + break; + case 276: + $function = 'COMBIN'; + $args = 2; + + break; + case 277: + $function = 'CONFIDENCE'; + $args = 3; + + break; + case 278: + $function = 'CRITBINOM'; + $args = 3; + + break; + case 279: + $function = 'EVEN'; + $args = 1; + + break; + case 280: + $function = 'EXPONDIST'; + $args = 3; + + break; + case 281: + $function = 'FDIST'; + $args = 3; + + break; + case 282: + $function = 'FINV'; + $args = 3; + + break; + case 283: + $function = 'FISHER'; + $args = 1; + + break; + case 284: + $function = 'FISHERINV'; + $args = 1; + + break; + case 285: + $function = 'FLOOR'; + $args = 2; + + break; + case 286: + $function = 'GAMMADIST'; + $args = 4; + + break; + case 287: + $function = 'GAMMAINV'; + $args = 3; + + break; + case 288: + $function = 'CEILING'; + $args = 2; + + break; + case 289: + $function = 'HYPGEOMDIST'; + $args = 4; + + break; + case 290: + $function = 'LOGNORMDIST'; + $args = 3; + + break; + case 291: + $function = 'LOGINV'; + $args = 3; + + break; + case 292: + $function = 'NEGBINOMDIST'; + $args = 3; + + break; + case 293: + $function = 'NORMDIST'; + $args = 4; + + break; + case 294: + $function = 'NORMSDIST'; + $args = 1; + + break; + case 295: + $function = 'NORMINV'; + $args = 3; + + break; + case 296: + $function = 'NORMSINV'; + $args = 1; + + break; + case 297: + $function = 'STANDARDIZE'; + $args = 3; + + break; + case 298: + $function = 'ODD'; + $args = 1; + + break; + case 299: + $function = 'PERMUT'; + $args = 2; + + break; + case 300: + $function = 'POISSON'; + $args = 3; + + break; + case 301: + $function = 'TDIST'; + $args = 3; + + break; + case 302: + $function = 'WEIBULL'; + $args = 4; + + break; + case 303: + $function = 'SUMXMY2'; + $args = 2; + + break; + case 304: + $function = 'SUMX2MY2'; + $args = 2; + + break; + case 305: + $function = 'SUMX2PY2'; + $args = 2; + + break; + case 306: + $function = 'CHITEST'; + $args = 2; + + break; + case 307: + $function = 'CORREL'; + $args = 2; + + break; + case 308: + $function = 'COVAR'; + $args = 2; + + break; + case 309: + $function = 'FORECAST'; + $args = 3; + + break; + case 310: + $function = 'FTEST'; + $args = 2; + + break; + case 311: + $function = 'INTERCEPT'; + $args = 2; + + break; + case 312: + $function = 'PEARSON'; + $args = 2; + + break; + case 313: + $function = 'RSQ'; + $args = 2; + + break; + case 314: + $function = 'STEYX'; + $args = 2; + + break; + case 315: + $function = 'SLOPE'; + $args = 2; + + break; + case 316: + $function = 'TTEST'; + $args = 4; + + break; + case 325: + $function = 'LARGE'; + $args = 2; + + break; + case 326: + $function = 'SMALL'; + $args = 2; + + break; + case 327: + $function = 'QUARTILE'; + $args = 2; + + break; + case 328: + $function = 'PERCENTILE'; + $args = 2; + + break; + case 331: + $function = 'TRIMMEAN'; + $args = 2; + + break; + case 332: + $function = 'TINV'; + $args = 2; + + break; + case 337: + $function = 'POWER'; + $args = 2; + + break; + case 342: + $function = 'RADIANS'; + $args = 1; + + break; + case 343: + $function = 'DEGREES'; + $args = 1; + + break; + case 346: + $function = 'COUNTIF'; + $args = 2; + + break; + case 347: + $function = 'COUNTBLANK'; + $args = 1; + + break; + case 350: + $function = 'ISPMT'; + $args = 4; + + break; + case 351: + $function = 'DATEDIF'; + $args = 3; + + break; + case 352: + $function = 'DATESTRING'; + $args = 1; + + break; + case 353: + $function = 'NUMBERSTRING'; + $args = 2; + + break; + case 360: + $function = 'PHONETIC'; + $args = 1; + + break; + case 368: + $function = 'BAHTTEXT'; + $args = 1; + + break; + default: + throw new Exception('Unrecognized function in formula'); + + break; + } + $data = ['function' => $function, 'args' => $args]; + + break; + case 0x22: // function with variable number of arguments + case 0x42: + case 0x62: + $name = 'tFuncV'; + $size = 4; + // offset: 1; size: 1; number of arguments + $args = ord($formulaData[1]); + // offset: 2: size: 2; index to built-in sheet function + $index = self::getUInt2d($formulaData, 2); + switch ($index) { + case 0: + $function = 'COUNT'; + + break; + case 1: + $function = 'IF'; + + break; + case 4: + $function = 'SUM'; + + break; + case 5: + $function = 'AVERAGE'; + + break; + case 6: + $function = 'MIN'; + + break; + case 7: + $function = 'MAX'; + + break; + case 8: + $function = 'ROW'; + + break; + case 9: + $function = 'COLUMN'; + + break; + case 11: + $function = 'NPV'; + + break; + case 12: + $function = 'STDEV'; + + break; + case 13: + $function = 'DOLLAR'; + + break; + case 14: + $function = 'FIXED'; + + break; + case 28: + $function = 'LOOKUP'; + + break; + case 29: + $function = 'INDEX'; + + break; + case 36: + $function = 'AND'; + + break; + case 37: + $function = 'OR'; + + break; + case 46: + $function = 'VAR'; + + break; + case 49: + $function = 'LINEST'; + + break; + case 50: + $function = 'TREND'; + + break; + case 51: + $function = 'LOGEST'; + + break; + case 52: + $function = 'GROWTH'; + + break; + case 56: + $function = 'PV'; + + break; + case 57: + $function = 'FV'; + + break; + case 58: + $function = 'NPER'; + + break; + case 59: + $function = 'PMT'; + + break; + case 60: + $function = 'RATE'; + + break; + case 62: + $function = 'IRR'; + + break; + case 64: + $function = 'MATCH'; + + break; + case 70: + $function = 'WEEKDAY'; + + break; + case 78: + $function = 'OFFSET'; + + break; + case 82: + $function = 'SEARCH'; + + break; + case 100: + $function = 'CHOOSE'; + + break; + case 101: + $function = 'HLOOKUP'; + + break; + case 102: + $function = 'VLOOKUP'; + + break; + case 109: + $function = 'LOG'; + + break; + case 115: + $function = 'LEFT'; + + break; + case 116: + $function = 'RIGHT'; + + break; + case 120: + $function = 'SUBSTITUTE'; + + break; + case 124: + $function = 'FIND'; + + break; + case 125: + $function = 'CELL'; + + break; + case 144: + $function = 'DDB'; + + break; + case 148: + $function = 'INDIRECT'; + + break; + case 167: + $function = 'IPMT'; + + break; + case 168: + $function = 'PPMT'; + + break; + case 169: + $function = 'COUNTA'; + + break; + case 183: + $function = 'PRODUCT'; + + break; + case 193: + $function = 'STDEVP'; + + break; + case 194: + $function = 'VARP'; + + break; + case 197: + $function = 'TRUNC'; + + break; + case 204: + $function = 'USDOLLAR'; + + break; + case 205: + $function = 'FINDB'; + + break; + case 206: + $function = 'SEARCHB'; + + break; + case 208: + $function = 'LEFTB'; + + break; + case 209: + $function = 'RIGHTB'; + + break; + case 216: + $function = 'RANK'; + + break; + case 219: + $function = 'ADDRESS'; + + break; + case 220: + $function = 'DAYS360'; + + break; + case 222: + $function = 'VDB'; + + break; + case 227: + $function = 'MEDIAN'; + + break; + case 228: + $function = 'SUMPRODUCT'; + + break; + case 247: + $function = 'DB'; + + break; + case 255: + $function = ''; + + break; + case 269: + $function = 'AVEDEV'; + + break; + case 270: + $function = 'BETADIST'; + + break; + case 272: + $function = 'BETAINV'; + + break; + case 317: + $function = 'PROB'; + + break; + case 318: + $function = 'DEVSQ'; + + break; + case 319: + $function = 'GEOMEAN'; + + break; + case 320: + $function = 'HARMEAN'; + + break; + case 321: + $function = 'SUMSQ'; + + break; + case 322: + $function = 'KURT'; + + break; + case 323: + $function = 'SKEW'; + + break; + case 324: + $function = 'ZTEST'; + + break; + case 329: + $function = 'PERCENTRANK'; + + break; + case 330: + $function = 'MODE'; + + break; + case 336: + $function = 'CONCATENATE'; + + break; + case 344: + $function = 'SUBTOTAL'; + + break; + case 345: + $function = 'SUMIF'; + + break; + case 354: + $function = 'ROMAN'; + + break; + case 358: + $function = 'GETPIVOTDATA'; + + break; + case 359: + $function = 'HYPERLINK'; + + break; + case 361: + $function = 'AVERAGEA'; + + break; + case 362: + $function = 'MAXA'; + + break; + case 363: + $function = 'MINA'; + + break; + case 364: + $function = 'STDEVPA'; + + break; + case 365: + $function = 'VARPA'; + + break; + case 366: + $function = 'STDEVA'; + + break; + case 367: + $function = 'VARA'; + + break; + default: + throw new Exception('Unrecognized function in formula'); + + break; + } + $data = ['function' => $function, 'args' => $args]; + + break; + case 0x23: // index to defined name + case 0x43: + case 0x63: + $name = 'tName'; + $size = 5; + // offset: 1; size: 2; one-based index to definedname record + $definedNameIndex = self::getUInt2d($formulaData, 1) - 1; + // offset: 2; size: 2; not used + $data = $this->definedname[$definedNameIndex]['name']; + + break; + case 0x24: // single cell reference e.g. A5 + case 0x44: + case 0x64: + $name = 'tRef'; + $size = 5; + $data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4)); + + break; + case 0x25: // cell range reference to cells in the same sheet (2d) + case 0x45: + case 0x65: + $name = 'tArea'; + $size = 9; + $data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8)); + + break; + case 0x26: // Constant reference sub-expression + case 0x46: + case 0x66: + $name = 'tMemArea'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = self::getUInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize)); + + break; + case 0x27: // Deleted constant reference sub-expression + case 0x47: + case 0x67: + $name = 'tMemErr'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = self::getUInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->getFormulaFromData(substr($formulaData, 7, $subSize)); + + break; + case 0x29: // Variable reference sub-expression + case 0x49: + case 0x69: + $name = 'tMemFunc'; + // offset: 1; size: 2; size of the following sub-expression + $subSize = self::getUInt2d($formulaData, 1); + $size = 3 + $subSize; + $data = $this->getFormulaFromData(substr($formulaData, 3, $subSize)); + + break; + case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places + case 0x4C: + case 0x6C: + $name = 'tRefN'; + $size = 5; + $data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell); + + break; + case 0x2D: // Relative 2d range reference + case 0x4D: + case 0x6D: + $name = 'tAreaN'; + $size = 9; + $data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell); + + break; + case 0x39: // External name + case 0x59: + case 0x79: + $name = 'tNameX'; + $size = 7; + // offset: 1; size: 2; index to REF entry in EXTERNSHEET record + // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record + $index = self::getUInt2d($formulaData, 3); + // assume index is to EXTERNNAME record + $data = $this->externalNames[$index - 1]['name']; + // offset: 5; size: 2; not used + break; + case 0x3A: // 3d reference to cell + case 0x5A: + case 0x7A: + $name = 'tRef3d'; + $size = 7; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1)); + // offset: 3; size: 4; cell address + $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4)); + + $data = "$sheetRange!$cellAddress"; + } catch (PhpSpreadsheetException $e) { + // deleted sheet reference + $data = '#REF!'; + } + + break; + case 0x3B: // 3d reference to cell range + case 0x5B: + case 0x7B: + $name = 'tArea3d'; + $size = 11; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1)); + // offset: 3; size: 8; cell address + $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8)); + + $data = "$sheetRange!$cellRangeAddress"; + } catch (PhpSpreadsheetException $e) { + // deleted sheet reference + $data = '#REF!'; + } + + break; + // Unknown cases // don't know how to deal with + default: + throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula'); + + break; + } + + return [ + 'id' => $id, + 'name' => $name, + 'size' => $size, + 'data' => $data, + ]; + } + + /** + * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2' + * section 3.3.4. + * + * @param string $cellAddressStructure + * + * @return string + */ + private function readBIFF8CellAddress($cellAddressStructure) + { + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $row = self::getUInt2d($cellAddressStructure, 0) + 1; + + // offset: 2; size: 2; index to column or column offset + relative flags + // bit: 7-0; mask 0x00FF; column index + $column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) { + $column = '$' . $column; + } + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } + + return $column . $row; + } + + /** + * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4. + * + * @param string $cellAddressStructure + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * + * @return string + */ + private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1') + { + [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell); + $baseCol = Coordinate::columnIndexFromString($baseCol) - 1; + + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $rowIndex = self::getUInt2d($cellAddressStructure, 0); + $row = self::getUInt2d($cellAddressStructure, 0) + 1; + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) { + // offset: 2; size: 2; index to column or column offset + relative flags + // bit: 7-0; mask 0x00FF; column index + $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2); + + $column = Coordinate::stringFromColumnIndex($colIndex + 1); + $column = '$' . $column; + } else { + // offset: 2; size: 2; index to column or column offset + relative flags + // bit: 7-0; mask 0x00FF; column index + $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2); + $colIndex = $baseCol + $relativeColIndex; + $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256; + $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256; + $column = Coordinate::stringFromColumnIndex($colIndex + 1); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } else { + $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536; + $row = $baseRow + $rowIndex; + } + + return $column . $row; + } + + /** + * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14. + * + * @param string $subData + * + * @return string + */ + private function readBIFF5CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = self::getUInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = self::getUInt2d($subData, 2) + 1; + + // offset: 4; size: 1; index to first column + $fc = ord($subData[4]); + + // offset: 5; size: 1; index to last column + $lc = ord($subData[5]); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = Coordinate::stringFromColumnIndex($fc + 1); + $lc = Coordinate::stringFromColumnIndex($lc + 1); + + if ($fr == $lr && $fc == $lc) { + return "$fc$fr"; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14. + * + * @param string $subData + * + * @return string + */ + private function readBIFF8CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = self::getUInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = self::getUInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column + $fc = self::getUInt2d($subData, 4); + + // offset: 6; size: 2; index to last column + $lc = self::getUInt2d($subData, 6); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = Coordinate::stringFromColumnIndex($fc + 1); + $lc = Coordinate::stringFromColumnIndex($lc + 1); + + if ($fr == $lr && $fc == $lc) { + return "$fc$fr"; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6' + * there are flags indicating whether column/row index is relative + * section 3.3.4. + * + * @param string $subData + * + * @return string + */ + private function readBIFF8CellRangeAddress($subData) + { + // todo: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767)) + $fr = self::getUInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767)) + $lr = self::getUInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($subData, 4))) { + $fc = '$' . $fc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($subData, 4))) { + $fr = '$' . $fr; + } + + // offset: 6; size: 2; index to last column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($subData, 6))) { + $lc = '$' . $lc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($subData, 6))) { + $lr = '$' . $lr; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4. + * + * @param string $subData + * @param string $baseCell Base cell + * + * @return string Cell range address + */ + private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1') + { + [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell); + $baseCol = Coordinate::columnIndexFromString($baseCol) - 1; + + // TODO: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; first row + $frIndex = self::getUInt2d($subData, 0); // adjust below + + // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767) + $lrIndex = self::getUInt2d($subData, 2); // adjust below + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($subData, 4))) { + // absolute column index + // offset: 4; size: 2; first column with relative/absolute flags + // bit: 7-0; mask 0x00FF; column index + $fcIndex = 0x00FF & self::getUInt2d($subData, 4); + $fc = Coordinate::stringFromColumnIndex($fcIndex + 1); + $fc = '$' . $fc; + } else { + // column offset + // offset: 4; size: 2; first column with relative/absolute flags + // bit: 7-0; mask 0x00FF; column index + $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4); + $fcIndex = $baseCol + $relativeFcIndex; + $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256; + $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256; + $fc = Coordinate::stringFromColumnIndex($fcIndex + 1); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($subData, 4))) { + // absolute row index + $fr = $frIndex + 1; + $fr = '$' . $fr; + } else { + // row offset + $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536; + $fr = $baseRow + $frIndex; + } + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & self::getUInt2d($subData, 6))) { + // absolute column index + // offset: 6; size: 2; last column with relative/absolute flags + // bit: 7-0; mask 0x00FF; column index + $lcIndex = 0x00FF & self::getUInt2d($subData, 6); + $lc = Coordinate::stringFromColumnIndex($lcIndex + 1); + $lc = '$' . $lc; + } else { + // column offset + // offset: 4; size: 2; first column with relative/absolute flags + // bit: 7-0; mask 0x00FF; column index + $relativeLcIndex = 0x00FF & self::getInt2d($subData, 4); + $lcIndex = $baseCol + $relativeLcIndex; + $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256; + $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256; + $lc = Coordinate::stringFromColumnIndex($lcIndex + 1); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & self::getUInt2d($subData, 6))) { + // absolute row index + $lr = $lrIndex + 1; + $lr = '$' . $lr; + } else { + // row offset + $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536; + $lr = $baseRow + $lrIndex; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Read BIFF8 cell range address list + * section 2.5.15. + * + * @param string $subData + * + * @return array + */ + private function readBIFF8CellRangeAddressList($subData) + { + $cellRangeAddresses = []; + + // offset: 0; size: 2; number of the following cell range addresses + $nm = self::getUInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8)); + $offset += 8; + } + + return [ + 'size' => 2 + 8 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ]; + } + + /** + * Read BIFF5 cell range address list + * section 2.5.15. + * + * @param string $subData + * + * @return array + */ + private function readBIFF5CellRangeAddressList($subData) + { + $cellRangeAddresses = []; + + // offset: 0; size: 2; number of the following cell range addresses + $nm = self::getUInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6)); + $offset += 6; + } + + return [ + 'size' => 2 + 6 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ]; + } + + /** + * Get a sheet range like Sheet1:Sheet3 from REF index + * Note: If there is only one sheet in the range, one gets e.g Sheet1 + * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets, + * in which case an Exception is thrown. + * + * @param int $index + * + * @return false|string + */ + private function readSheetRangeByRefIndex($index) + { + if (isset($this->ref[$index])) { + $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type']; + + switch ($type) { + case 'internal': + // check if we have a deleted 3d reference + if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF || $this->ref[$index]['lastSheetIndex'] == 0xFFFF) { + throw new Exception('Deleted sheet reference'); + } + + // we have normal sheet range (collapsed or uncollapsed) + $firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name']; + $lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name']; + + if ($firstSheetName == $lastSheetName) { + // collapsed sheet range + $sheetRange = $firstSheetName; + } else { + $sheetRange = "$firstSheetName:$lastSheetName"; + } + + // escape the single-quotes + $sheetRange = str_replace("'", "''", $sheetRange); + + // if there are special characters, we need to enclose the range in single-quotes + // todo: check if we have identified the whole set of special characters + // it seems that the following characters are not accepted for sheet names + // and we may assume that they are not present: []*/:\? + if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) { + $sheetRange = "'$sheetRange'"; + } + + return $sheetRange; + + break; + default: + // TODO: external sheet support + throw new Exception('Xls reader only supports internal sheets in formulas'); + + break; + } + } + + return false; + } + + /** + * read BIFF8 constant value array from array data + * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40] + * section 2.5.8. + * + * @param string $arrayData + * + * @return array + */ + private static function readBIFF8ConstantArray($arrayData) + { + // offset: 0; size: 1; number of columns decreased by 1 + $nc = ord($arrayData[0]); + + // offset: 1; size: 2; number of rows decreased by 1 + $nr = self::getUInt2d($arrayData, 1); + $size = 3; // initialize + $arrayData = substr($arrayData, 3); + + // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values + $matrixChunks = []; + for ($r = 1; $r <= $nr + 1; ++$r) { + $items = []; + for ($c = 1; $c <= $nc + 1; ++$c) { + $constant = self::readBIFF8Constant($arrayData); + $items[] = $constant['value']; + $arrayData = substr($arrayData, $constant['size']); + $size += $constant['size']; + } + $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"' + } + $matrix = '{' . implode(';', $matrixChunks) . '}'; + + return [ + 'value' => $matrix, + 'size' => $size, + ]; + } + + /** + * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value' + * section 2.5.7 + * returns e.g. ['value' => '5', 'size' => 9]. + * + * @param string $valueData + * + * @return array + */ + private static function readBIFF8Constant($valueData) + { + // offset: 0; size: 1; identifier for type of constant + $identifier = ord($valueData[0]); + + switch ($identifier) { + case 0x00: // empty constant (what is this?) + $value = ''; + $size = 9; + + break; + case 0x01: // number + // offset: 1; size: 8; IEEE 754 floating-point value + $value = self::extractNumber(substr($valueData, 1, 8)); + $size = 9; + + break; + case 0x02: // string value + // offset: 1; size: var; Unicode string, 16-bit string length + $string = self::readUnicodeStringLong(substr($valueData, 1)); + $value = '"' . $string['value'] . '"'; + $size = 1 + $string['size']; + + break; + case 0x04: // boolean + // offset: 1; size: 1; 0 = FALSE, 1 = TRUE + if (ord($valueData[1])) { + $value = 'TRUE'; + } else { + $value = 'FALSE'; + } + $size = 9; + + break; + case 0x10: // error code + // offset: 1; size: 1; error code + $value = Xls\ErrorCode::lookup(ord($valueData[1])); + $size = 9; + + break; + } + + return [ + 'value' => $value, + 'size' => $size, + ]; + } + + /** + * Extract RGB color + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4. + * + * @param string $rgb Encoded RGB value (4 bytes) + * + * @return array + */ + private static function readRGB($rgb) + { + // offset: 0; size 1; Red component + $r = ord($rgb[0]); + + // offset: 1; size: 1; Green component + $g = ord($rgb[1]); + + // offset: 2; size: 1; Blue component + $b = ord($rgb[2]); + + // HEX notation, e.g. 'FF00FC' + $rgb = sprintf('%02X%02X%02X', $r, $g, $b); + + return ['rgb' => $rgb]; + } + + /** + * Read byte string (8-bit string length) + * OpenOffice documentation: 2.5.2. + * + * @param string $subData + * + * @return array + */ + private function readByteStringShort($subData) + { + // offset: 0; size: 1; length of the string (character count) + $ln = ord($subData[0]); + + // offset: 1: size: var; character array (8-bit characters) + $value = $this->decodeCodepage(substr($subData, 1, $ln)); + + return [ + 'value' => $value, + 'size' => 1 + $ln, // size in bytes of data structure + ]; + } + + /** + * Read byte string (16-bit string length) + * OpenOffice documentation: 2.5.2. + * + * @param string $subData + * + * @return array + */ + private function readByteStringLong($subData) + { + // offset: 0; size: 2; length of the string (character count) + $ln = self::getUInt2d($subData, 0); + + // offset: 2: size: var; character array (8-bit characters) + $value = $this->decodeCodepage(substr($subData, 2)); + + //return $string; + return [ + 'value' => $value, + 'size' => 2 + $ln, // size in bytes of data structure + ]; + } + + /** + * Extracts an Excel Unicode short string (8-bit string length) + * OpenOffice documentation: 2.5.3 + * function will automatically find out where the Unicode string ends. + * + * @param string $subData + * + * @return array + */ + private static function readUnicodeStringShort($subData) + { + $value = ''; + + // offset: 0: size: 1; length of the string (character count) + $characterCount = ord($subData[0]); + + $string = self::readUnicodeString(substr($subData, 1), $characterCount); + + // add 1 for the string length + ++$string['size']; + + return $string; + } + + /** + * Extracts an Excel Unicode long string (16-bit string length) + * OpenOffice documentation: 2.5.3 + * this function is under construction, needs to support rich text, and Asian phonetic settings. + * + * @param string $subData + * + * @return array + */ + private static function readUnicodeStringLong($subData) + { + $value = ''; + + // offset: 0: size: 2; length of the string (character count) + $characterCount = self::getUInt2d($subData, 0); + + $string = self::readUnicodeString(substr($subData, 2), $characterCount); + + // add 2 for the string length + $string['size'] += 2; + + return $string; + } + + /** + * Read Unicode string with no string length field, but with known character count + * this function is under construction, needs to support rich text, and Asian phonetic settings + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3. + * + * @param string $subData + * @param int $characterCount + * + * @return array + */ + private static function readUnicodeString($subData, $characterCount) + { + $value = ''; + + // offset: 0: size: 1; option flags + // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit) + $isCompressed = !((0x01 & ord($subData[0])) >> 0); + + // bit: 2; mask: 0x04; Asian phonetic settings + $hasAsian = (0x04) & ord($subData[0]) >> 2; + + // bit: 3; mask: 0x08; Rich-Text settings + $hasRichText = (0x08) & ord($subData[0]) >> 3; + + // offset: 1: size: var; character array + // this offset assumes richtext and Asian phonetic settings are off which is generally wrong + // needs to be fixed + $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed); + + return [ + 'value' => $value, + 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags + ]; + } + + /** + * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas. + * Example: hello"world --> "hello""world". + * + * @param string $value UTF-8 encoded string + * + * @return string + */ + private static function UTF8toExcelDoubleQuoted($value) + { + return '"' . str_replace('"', '""', $value) . '"'; + } + + /** + * Reads first 8 bytes of a string and return IEEE 754 float. + * + * @param string $data Binary string that is at least 8 bytes long + * + * @return float + */ + private static function extractNumber($data) + { + $rknumhigh = self::getInt4d($data, 4); + $rknumlow = self::getInt4d($data, 0); + $sign = ($rknumhigh & 0x80000000) >> 31; + $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023; + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; + $mantissalow2 = ($rknumlow & 0x7fffffff); + $value = $mantissa / 2 ** (20 - $exp); + + if ($mantissalow1 != 0) { + $value += 1 / 2 ** (21 - $exp); + } + + $value += $mantissalow2 / 2 ** (52 - $exp); + if ($sign) { + $value *= -1; + } + + return $value; + } + + /** + * @param int $rknum + * + * @return float + */ + private static function getIEEE754($rknum) + { + if (($rknum & 0x02) != 0) { + $value = $rknum >> 2; + } else { + // changes by mmp, info on IEEE754 encoding from + // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html + // The RK format calls for using only the most significant 30 bits + // of the 64 bit floating point value. The other 34 bits are assumed + // to be 0 so we use the upper 30 bits of $rknum as follows... + $sign = ($rknum & 0x80000000) >> 31; + $exp = ($rknum & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); + $value = $mantissa / 2 ** (20 - ($exp - 1023)); + if ($sign) { + $value = -1 * $value; + } + //end of changes by mmp + } + if (($rknum & 0x01) != 0) { + $value /= 100; + } + + return $value; + } + + /** + * Get UTF-8 string from (compressed or uncompressed) UTF-16 string. + * + * @param string $string + * @param bool $compressed + * + * @return string + */ + private static function encodeUTF16($string, $compressed = false) + { + if ($compressed) { + $string = self::uncompressByteString($string); + } + + return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE'); + } + + /** + * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8. + * + * @param string $string + * + * @return string + */ + private static function uncompressByteString($string) + { + $uncompressedString = ''; + $strLen = strlen($string); + for ($i = 0; $i < $strLen; ++$i) { + $uncompressedString .= $string[$i] . "\0"; + } + + return $uncompressedString; + } + + /** + * Convert string to UTF-8. Only used for BIFF5. + * + * @param string $string + * + * @return string + */ + private function decodeCodepage($string) + { + return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage); + } + + /** + * Read 16-bit unsigned integer. + * + * @param string $data + * @param int $pos + * + * @return int + */ + public static function getUInt2d($data, $pos) + { + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); + } + + /** + * Read 16-bit signed integer. + * + * @param string $data + * @param int $pos + * + * @return int + */ + public static function getInt2d($data, $pos) + { + return unpack('s', $data[$pos] . $data[$pos + 1])[1]; + } + + /** + * Read 32-bit signed integer. + * + * @param string $data + * @param int $pos + * + * @return int + */ + public static function getInt4d($data, $pos) + { + // FIX: represent numbers correctly on 64-bit system + // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 + // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems + $_or_24 = ord($data[$pos + 3]); + if ($_or_24 >= 128) { + // negative number + $_ord_24 = -abs((256 - $_or_24) << 24); + } else { + $_ord_24 = ($_or_24 & 127) << 24; + } + + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; + } + + private function parseRichText($is) + { + $value = new RichText(); + $value->createText($is); + + return $value; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php new file mode 100644 index 00000000000..c45f88c72b5 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php @@ -0,0 +1,36 @@ + 'FF0000'] + */ + public static function map($color, $palette, $version) + { + if ($color <= 0x07 || $color >= 0x40) { + // special built-in color + return Color\BuiltIn::lookup($color); + } elseif (isset($palette, $palette[$color - 8])) { + // palette color, color index 0x08 maps to pallete index 0 + return $palette[$color - 8]; + } + + // default color table + if ($version == Xls::XLS_BIFF8) { + return Color\BIFF8::lookup($color); + } + + // BIFF5 + return Color\BIFF5::lookup($color); + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php new file mode 100644 index 00000000000..743d938773e --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php @@ -0,0 +1,81 @@ + '000000', + 0x09 => 'FFFFFF', + 0x0A => 'FF0000', + 0x0B => '00FF00', + 0x0C => '0000FF', + 0x0D => 'FFFF00', + 0x0E => 'FF00FF', + 0x0F => '00FFFF', + 0x10 => '800000', + 0x11 => '008000', + 0x12 => '000080', + 0x13 => '808000', + 0x14 => '800080', + 0x15 => '008080', + 0x16 => 'C0C0C0', + 0x17 => '808080', + 0x18 => '8080FF', + 0x19 => '802060', + 0x1A => 'FFFFC0', + 0x1B => 'A0E0F0', + 0x1C => '600080', + 0x1D => 'FF8080', + 0x1E => '0080C0', + 0x1F => 'C0C0FF', + 0x20 => '000080', + 0x21 => 'FF00FF', + 0x22 => 'FFFF00', + 0x23 => '00FFFF', + 0x24 => '800080', + 0x25 => '800000', + 0x26 => '008080', + 0x27 => '0000FF', + 0x28 => '00CFFF', + 0x29 => '69FFFF', + 0x2A => 'E0FFE0', + 0x2B => 'FFFF80', + 0x2C => 'A6CAF0', + 0x2D => 'DD9CB3', + 0x2E => 'B38FEE', + 0x2F => 'E3E3E3', + 0x30 => '2A6FF9', + 0x31 => '3FB8CD', + 0x32 => '488436', + 0x33 => '958C41', + 0x34 => '8E5E42', + 0x35 => 'A0627A', + 0x36 => '624FAC', + 0x37 => '969696', + 0x38 => '1D2FBE', + 0x39 => '286676', + 0x3A => '004500', + 0x3B => '453E01', + 0x3C => '6A2813', + 0x3D => '85396A', + 0x3E => '4A3285', + 0x3F => '424242', + ]; + + /** + * Map color array from BIFF5 built-in color index. + * + * @param int $color + * + * @return array + */ + public static function lookup($color) + { + if (isset(self::$map[$color])) { + return ['rgb' => self::$map[$color]]; + } + + return ['rgb' => '000000']; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php new file mode 100644 index 00000000000..5c109fb071f --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php @@ -0,0 +1,81 @@ + '000000', + 0x09 => 'FFFFFF', + 0x0A => 'FF0000', + 0x0B => '00FF00', + 0x0C => '0000FF', + 0x0D => 'FFFF00', + 0x0E => 'FF00FF', + 0x0F => '00FFFF', + 0x10 => '800000', + 0x11 => '008000', + 0x12 => '000080', + 0x13 => '808000', + 0x14 => '800080', + 0x15 => '008080', + 0x16 => 'C0C0C0', + 0x17 => '808080', + 0x18 => '9999FF', + 0x19 => '993366', + 0x1A => 'FFFFCC', + 0x1B => 'CCFFFF', + 0x1C => '660066', + 0x1D => 'FF8080', + 0x1E => '0066CC', + 0x1F => 'CCCCFF', + 0x20 => '000080', + 0x21 => 'FF00FF', + 0x22 => 'FFFF00', + 0x23 => '00FFFF', + 0x24 => '800080', + 0x25 => '800000', + 0x26 => '008080', + 0x27 => '0000FF', + 0x28 => '00CCFF', + 0x29 => 'CCFFFF', + 0x2A => 'CCFFCC', + 0x2B => 'FFFF99', + 0x2C => '99CCFF', + 0x2D => 'FF99CC', + 0x2E => 'CC99FF', + 0x2F => 'FFCC99', + 0x30 => '3366FF', + 0x31 => '33CCCC', + 0x32 => '99CC00', + 0x33 => 'FFCC00', + 0x34 => 'FF9900', + 0x35 => 'FF6600', + 0x36 => '666699', + 0x37 => '969696', + 0x38 => '003366', + 0x39 => '339966', + 0x3A => '003300', + 0x3B => '333300', + 0x3C => '993300', + 0x3D => '993366', + 0x3E => '333399', + 0x3F => '333333', + ]; + + /** + * Map color array from BIFF8 built-in color index. + * + * @param int $color + * + * @return array + */ + public static function lookup($color) + { + if (isset(self::$map[$color])) { + return ['rgb' => self::$map[$color]]; + } + + return ['rgb' => '000000']; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php new file mode 100644 index 00000000000..90d50e336bb --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php @@ -0,0 +1,35 @@ + '000000', + 0x01 => 'FFFFFF', + 0x02 => 'FF0000', + 0x03 => '00FF00', + 0x04 => '0000FF', + 0x05 => 'FFFF00', + 0x06 => 'FF00FF', + 0x07 => '00FFFF', + 0x40 => '000000', // system window text color + 0x41 => 'FFFFFF', // system window background color + ]; + + /** + * Map built-in color to RGB value. + * + * @param int $color Indexed color + * + * @return array + */ + public static function lookup($color) + { + if (isset(self::$map[$color])) { + return ['rgb' => self::$map[$color]]; + } + + return ['rgb' => '000000']; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php new file mode 100644 index 00000000000..7daf7230ff0 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php @@ -0,0 +1,32 @@ + '#NULL!', + 0x07 => '#DIV/0!', + 0x0F => '#VALUE!', + 0x17 => '#REF!', + 0x1D => '#NAME?', + 0x24 => '#NUM!', + 0x2A => '#N/A', + ]; + + /** + * Map error code, e.g. '#N/A'. + * + * @param int $code + * + * @return bool|string + */ + public static function lookup($code) + { + if (isset(self::$map[$code])) { + return self::$map[$code]; + } + + return false; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php new file mode 100644 index 00000000000..306fc8f1f74 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php @@ -0,0 +1,677 @@ +object = $object; + } + + /** + * Load Escher stream data. May be a partial Escher stream. + * + * @param string $data + * + * @return BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer + */ + public function load($data) + { + $this->data = $data; + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->dataSize = strlen($this->data); + + $this->pos = 0; + + // Parse Escher stream + while ($this->pos < $this->dataSize) { + // offset: 2; size: 2: Record Type + $fbt = Xls::getUInt2d($this->data, $this->pos + 2); + + switch ($fbt) { + case self::DGGCONTAINER: + $this->readDggContainer(); + + break; + case self::DGG: + $this->readDgg(); + + break; + case self::BSTORECONTAINER: + $this->readBstoreContainer(); + + break; + case self::BSE: + $this->readBSE(); + + break; + case self::BLIPJPEG: + $this->readBlipJPEG(); + + break; + case self::BLIPPNG: + $this->readBlipPNG(); + + break; + case self::OPT: + $this->readOPT(); + + break; + case self::TERTIARYOPT: + $this->readTertiaryOPT(); + + break; + case self::SPLITMENUCOLORS: + $this->readSplitMenuColors(); + + break; + case self::DGCONTAINER: + $this->readDgContainer(); + + break; + case self::DG: + $this->readDg(); + + break; + case self::SPGRCONTAINER: + $this->readSpgrContainer(); + + break; + case self::SPCONTAINER: + $this->readSpContainer(); + + break; + case self::SPGR: + $this->readSpgr(); + + break; + case self::SP: + $this->readSp(); + + break; + case self::CLIENTTEXTBOX: + $this->readClientTextbox(); + + break; + case self::CLIENTANCHOR: + $this->readClientAnchor(); + + break; + case self::CLIENTDATA: + $this->readClientData(); + + break; + default: + $this->readDefault(); + + break; + } + } + + return $this->object; + } + + /** + * Read a generic record. + */ + private function readDefault(): void + { + // offset 0; size: 2; recVer and recInstance + $verInstance = Xls::getUInt2d($this->data, $this->pos); + + // offset: 2; size: 2: Record Type + $fbt = Xls::getUInt2d($this->data, $this->pos + 2); + + // bit: 0-3; mask: 0x000F; recVer + $recVer = (0x000F & $verInstance) >> 0; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read DggContainer record (Drawing Group Container). + */ + private function readDggContainer(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // record is a container, read contents + $dggContainer = new DggContainer(); + $this->object->setDggContainer($dggContainer); + $reader = new self($dggContainer); + $reader->load($recordData); + } + + /** + * Read Dgg record (Drawing Group). + */ + private function readDgg(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read BstoreContainer record (Blip Store Container). + */ + private function readBstoreContainer(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // record is a container, read contents + $bstoreContainer = new BstoreContainer(); + $this->object->setBstoreContainer($bstoreContainer); + $reader = new self($bstoreContainer); + $reader->load($recordData); + } + + /** + * Read BSE record. + */ + private function readBSE(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // add BSE to BstoreContainer + $BSE = new BSE(); + $this->object->addBSE($BSE); + + $BSE->setBLIPType($recInstance); + + // offset: 0; size: 1; btWin32 (MSOBLIPTYPE) + $btWin32 = ord($recordData[0]); + + // offset: 1; size: 1; btWin32 (MSOBLIPTYPE) + $btMacOS = ord($recordData[1]); + + // offset: 2; size: 16; MD4 digest + $rgbUid = substr($recordData, 2, 16); + + // offset: 18; size: 2; tag + $tag = Xls::getUInt2d($recordData, 18); + + // offset: 20; size: 4; size of BLIP in bytes + $size = Xls::getInt4d($recordData, 20); + + // offset: 24; size: 4; number of references to this BLIP + $cRef = Xls::getInt4d($recordData, 24); + + // offset: 28; size: 4; MSOFO file offset + $foDelay = Xls::getInt4d($recordData, 28); + + // offset: 32; size: 1; unused1 + $unused1 = ord($recordData[32]); + + // offset: 33; size: 1; size of nameData in bytes (including null terminator) + $cbName = ord($recordData[33]); + + // offset: 34; size: 1; unused2 + $unused2 = ord($recordData[34]); + + // offset: 35; size: 1; unused3 + $unused3 = ord($recordData[35]); + + // offset: 36; size: $cbName; nameData + $nameData = substr($recordData, 36, $cbName); + + // offset: 36 + $cbName, size: var; the BLIP data + $blipData = substr($recordData, 36 + $cbName); + + // record is a container, read contents + $reader = new self($BSE); + $reader->load($blipData); + } + + /** + * Read BlipJPEG record. Holds raw JPEG image data. + */ + private function readBlipJPEG(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + $pos = 0; + + // offset: 0; size: 16; rgbUid1 (MD4 digest of) + $rgbUid1 = substr($recordData, 0, 16); + $pos += 16; + + // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3 + if (in_array($recInstance, [0x046B, 0x06E3])) { + $rgbUid2 = substr($recordData, 16, 16); + $pos += 16; + } + + // offset: var; size: 1; tag + $tag = ord($recordData[$pos]); + ++$pos; + + // offset: var; size: var; the raw image data + $data = substr($recordData, $pos); + + $blip = new Blip(); + $blip->setData($data); + + $this->object->setBlip($blip); + } + + /** + * Read BlipPNG record. Holds raw PNG image data. + */ + private function readBlipPNG(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + $pos = 0; + + // offset: 0; size: 16; rgbUid1 (MD4 digest of) + $rgbUid1 = substr($recordData, 0, 16); + $pos += 16; + + // offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3 + if ($recInstance == 0x06E1) { + $rgbUid2 = substr($recordData, 16, 16); + $pos += 16; + } + + // offset: var; size: 1; tag + $tag = ord($recordData[$pos]); + ++$pos; + + // offset: var; size: var; the raw image data + $data = substr($recordData, $pos); + + $blip = new Blip(); + $blip->setData($data); + + $this->object->setBlip($blip); + } + + /** + * Read OPT record. This record may occur within DggContainer record or SpContainer. + */ + private function readOPT(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + $this->readOfficeArtRGFOPTE($recordData, $recInstance); + } + + /** + * Read TertiaryOPT record. + */ + private function readTertiaryOPT(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read SplitMenuColors record. + */ + private function readSplitMenuColors(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read DgContainer record (Drawing Container). + */ + private function readDgContainer(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // record is a container, read contents + $dgContainer = new DgContainer(); + $this->object->setDgContainer($dgContainer); + $reader = new self($dgContainer); + $escher = $reader->load($recordData); + } + + /** + * Read Dg record (Drawing). + */ + private function readDg(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read SpgrContainer record (Shape Group Container). + */ + private function readSpgrContainer(): void + { + // context is either context DgContainer or SpgrContainer + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // record is a container, read contents + $spgrContainer = new SpgrContainer(); + + if ($this->object instanceof DgContainer) { + // DgContainer + $this->object->setSpgrContainer($spgrContainer); + } else { + // SpgrContainer + $this->object->addChild($spgrContainer); + } + + $reader = new self($spgrContainer); + $escher = $reader->load($recordData); + } + + /** + * Read SpContainer record (Shape Container). + */ + private function readSpContainer(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // add spContainer to spgrContainer + $spContainer = new SpContainer(); + $this->object->addChild($spContainer); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // record is a container, read contents + $reader = new self($spContainer); + $escher = $reader->load($recordData); + } + + /** + * Read Spgr record (Shape Group). + */ + private function readSpgr(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read Sp record (Shape). + */ + private function readSp(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read ClientTextbox record. + */ + private function readClientTextbox(): void + { + // offset: 0; size: 2; recVer and recInstance + + // bit: 4-15; mask: 0xFFF0; recInstance + $recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4; + + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet. + */ + private function readClientAnchor(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + + // offset: 2; size: 2; upper-left corner column index (0-based) + $c1 = Xls::getUInt2d($recordData, 2); + + // offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width + $startOffsetX = Xls::getUInt2d($recordData, 4); + + // offset: 6; size: 2; upper-left corner row index (0-based) + $r1 = Xls::getUInt2d($recordData, 6); + + // offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height + $startOffsetY = Xls::getUInt2d($recordData, 8); + + // offset: 10; size: 2; bottom-right corner column index (0-based) + $c2 = Xls::getUInt2d($recordData, 10); + + // offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width + $endOffsetX = Xls::getUInt2d($recordData, 12); + + // offset: 14; size: 2; bottom-right corner row index (0-based) + $r2 = Xls::getUInt2d($recordData, 14); + + // offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height + $endOffsetY = Xls::getUInt2d($recordData, 16); + + // set the start coordinates + $this->object->setStartCoordinates(Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1)); + + // set the start offsetX + $this->object->setStartOffsetX($startOffsetX); + + // set the start offsetY + $this->object->setStartOffsetY($startOffsetY); + + // set the end coordinates + $this->object->setEndCoordinates(Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1)); + + // set the end offsetX + $this->object->setEndOffsetX($endOffsetX); + + // set the end offsetY + $this->object->setEndOffsetY($endOffsetY); + } + + /** + * Read ClientData record. + */ + private function readClientData(): void + { + $length = Xls::getInt4d($this->data, $this->pos + 4); + $recordData = substr($this->data, $this->pos + 8, $length); + + // move stream pointer to next record + $this->pos += 8 + $length; + } + + /** + * Read OfficeArtRGFOPTE table of property-value pairs. + * + * @param string $data Binary data + * @param int $n Number of properties + */ + private function readOfficeArtRGFOPTE($data, $n): void + { + $splicedComplexData = substr($data, 6 * $n); + + // loop through property-value pairs + for ($i = 0; $i < $n; ++$i) { + // read 6 bytes at a time + $fopte = substr($data, 6 * $i, 6); + + // offset: 0; size: 2; opid + $opid = Xls::getUInt2d($fopte, 0); + + // bit: 0-13; mask: 0x3FFF; opid.opid + $opidOpid = (0x3FFF & $opid) >> 0; + + // bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier + $opidFBid = (0x4000 & $opid) >> 14; + + // bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data + $opidFComplex = (0x8000 & $opid) >> 15; + + // offset: 2; size: 4; the value for this property + $op = Xls::getInt4d($fopte, 2); + + if ($opidFComplex) { + $complexData = substr($splicedComplexData, 0, $op); + $splicedComplexData = substr($splicedComplexData, $op); + + // we store string value with complex data + $value = $complexData; + } else { + // we store integer value + $value = $op; + } + + $this->object->setOPT($opidOpid, $value); + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php new file mode 100644 index 00000000000..c0417ba6932 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php @@ -0,0 +1,184 @@ +reset(); + } + + /** + * Reset the MD5 stream context. + */ + public function reset(): void + { + $this->a = 0x67452301; + $this->b = 0xEFCDAB89; + $this->c = 0x98BADCFE; + $this->d = 0x10325476; + } + + /** + * Get MD5 stream context. + * + * @return string + */ + public function getContext() + { + $s = ''; + foreach (['a', 'b', 'c', 'd'] as $i) { + $v = $this->{$i}; + $s .= chr($v & 0xff); + $s .= chr(($v >> 8) & 0xff); + $s .= chr(($v >> 16) & 0xff); + $s .= chr(($v >> 24) & 0xff); + } + + return $s; + } + + /** + * Add data to context. + * + * @param string $data Data to add + */ + public function add($data): void + { + $words = array_values(unpack('V16', $data)); + + $A = $this->a; + $B = $this->b; + $C = $this->c; + $D = $this->d; + + $F = ['self', 'f']; + $G = ['self', 'g']; + $H = ['self', 'h']; + $I = ['self', 'i']; + + // ROUND 1 + self::step($F, $A, $B, $C, $D, $words[0], 7, 0xd76aa478); + self::step($F, $D, $A, $B, $C, $words[1], 12, 0xe8c7b756); + self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070db); + self::step($F, $B, $C, $D, $A, $words[3], 22, 0xc1bdceee); + self::step($F, $A, $B, $C, $D, $words[4], 7, 0xf57c0faf); + self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787c62a); + self::step($F, $C, $D, $A, $B, $words[6], 17, 0xa8304613); + self::step($F, $B, $C, $D, $A, $words[7], 22, 0xfd469501); + self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098d8); + self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8b44f7af); + self::step($F, $C, $D, $A, $B, $words[10], 17, 0xffff5bb1); + self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895cd7be); + self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6b901122); + self::step($F, $D, $A, $B, $C, $words[13], 12, 0xfd987193); + self::step($F, $C, $D, $A, $B, $words[14], 17, 0xa679438e); + self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49b40821); + + // ROUND 2 + self::step($G, $A, $B, $C, $D, $words[1], 5, 0xf61e2562); + self::step($G, $D, $A, $B, $C, $words[6], 9, 0xc040b340); + self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265e5a51); + self::step($G, $B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa); + self::step($G, $A, $B, $C, $D, $words[5], 5, 0xd62f105d); + self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453); + self::step($G, $C, $D, $A, $B, $words[15], 14, 0xd8a1e681); + self::step($G, $B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8); + self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21e1cde6); + self::step($G, $D, $A, $B, $C, $words[14], 9, 0xc33707d6); + self::step($G, $C, $D, $A, $B, $words[3], 14, 0xf4d50d87); + self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455a14ed); + self::step($G, $A, $B, $C, $D, $words[13], 5, 0xa9e3e905); + self::step($G, $D, $A, $B, $C, $words[2], 9, 0xfcefa3f8); + self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676f02d9); + self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a); + + // ROUND 3 + self::step($H, $A, $B, $C, $D, $words[5], 4, 0xfffa3942); + self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771f681); + self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6d9d6122); + self::step($H, $B, $C, $D, $A, $words[14], 23, 0xfde5380c); + self::step($H, $A, $B, $C, $D, $words[1], 4, 0xa4beea44); + self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4bdecfa9); + self::step($H, $C, $D, $A, $B, $words[7], 16, 0xf6bb4b60); + self::step($H, $B, $C, $D, $A, $words[10], 23, 0xbebfbc70); + self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289b7ec6); + self::step($H, $D, $A, $B, $C, $words[0], 11, 0xeaa127fa); + self::step($H, $C, $D, $A, $B, $words[3], 16, 0xd4ef3085); + self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881d05); + self::step($H, $A, $B, $C, $D, $words[9], 4, 0xd9d4d039); + self::step($H, $D, $A, $B, $C, $words[12], 11, 0xe6db99e5); + self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1fa27cf8); + self::step($H, $B, $C, $D, $A, $words[2], 23, 0xc4ac5665); + + // ROUND 4 + self::step($I, $A, $B, $C, $D, $words[0], 6, 0xf4292244); + self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432aff97); + self::step($I, $C, $D, $A, $B, $words[14], 15, 0xab9423a7); + self::step($I, $B, $C, $D, $A, $words[5], 21, 0xfc93a039); + self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655b59c3); + self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8f0ccc92); + self::step($I, $C, $D, $A, $B, $words[10], 15, 0xffeff47d); + self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845dd1); + self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6fa87e4f); + self::step($I, $D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0); + self::step($I, $C, $D, $A, $B, $words[6], 15, 0xa3014314); + self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4e0811a1); + self::step($I, $A, $B, $C, $D, $words[4], 6, 0xf7537e82); + self::step($I, $D, $A, $B, $C, $words[11], 10, 0xbd3af235); + self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb); + self::step($I, $B, $C, $D, $A, $words[9], 21, 0xeb86d391); + + $this->a = ($this->a + $A) & 0xffffffff; + $this->b = ($this->b + $B) & 0xffffffff; + $this->c = ($this->c + $C) & 0xffffffff; + $this->d = ($this->d + $D) & 0xffffffff; + } + + private static function f($X, $Y, $Z) + { + return ($X & $Y) | ((~$X) & $Z); // X AND Y OR NOT X AND Z + } + + private static function g($X, $Y, $Z) + { + return ($X & $Z) | ($Y & (~$Z)); // X AND Z OR Y AND NOT Z + } + + private static function h($X, $Y, $Z) + { + return $X ^ $Y ^ $Z; // X XOR Y XOR Z + } + + private static function i($X, $Y, $Z) + { + return $Y ^ ($X | (~$Z)); // Y XOR (X OR NOT Z) + } + + private static function step($func, &$A, $B, $C, $D, $M, $s, $t): void + { + $A = ($A + call_user_func($func, $B, $C, $D) + $M + $t) & 0xffffffff; + $A = self::rotate($A, $s); + $A = ($B + $A) & 0xffffffff; + } + + private static function rotate($decimal, $bits) + { + $binary = str_pad(decbin($decimal), 32, '0', STR_PAD_LEFT); + + return bindec(substr($binary, $bits) . substr($binary, 0, $bits)); + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php new file mode 100644 index 00000000000..691aca7c398 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php @@ -0,0 +1,61 @@ +i = 0; $this->i < 256; ++$this->i) { + $this->s[$this->i] = $this->i; + } + + $this->j = 0; + for ($this->i = 0; $this->i < 256; ++$this->i) { + $this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256; + $t = $this->s[$this->i]; + $this->s[$this->i] = $this->s[$this->j]; + $this->s[$this->j] = $t; + } + $this->i = $this->j = 0; + } + + /** + * Symmetric decryption/encryption function. + * + * @param string $data Data to encrypt/decrypt + * + * @return string + */ + public function RC4($data) + { + $len = strlen($data); + for ($c = 0; $c < $len; ++$c) { + $this->i = ($this->i + 1) % 256; + $this->j = ($this->j + $this->s[$this->i]) % 256; + $t = $this->s[$this->i]; + $this->s[$this->i] = $this->s[$this->j]; + $this->s[$this->j] = $t; + + $t = ($this->s[$this->i] + $this->s[$this->j]) % 256; + + $data[$c] = chr(ord($data[$c]) ^ $this->s[$t]); + } + + return $data; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php new file mode 100644 index 00000000000..91cbe36f136 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php @@ -0,0 +1,42 @@ + StyleBorder::BORDER_NONE, + 0x01 => StyleBorder::BORDER_THIN, + 0x02 => StyleBorder::BORDER_MEDIUM, + 0x03 => StyleBorder::BORDER_DASHED, + 0x04 => StyleBorder::BORDER_DOTTED, + 0x05 => StyleBorder::BORDER_THICK, + 0x06 => StyleBorder::BORDER_DOUBLE, + 0x07 => StyleBorder::BORDER_HAIR, + 0x08 => StyleBorder::BORDER_MEDIUMDASHED, + 0x09 => StyleBorder::BORDER_DASHDOT, + 0x0A => StyleBorder::BORDER_MEDIUMDASHDOT, + 0x0B => StyleBorder::BORDER_DASHDOTDOT, + 0x0C => StyleBorder::BORDER_MEDIUMDASHDOTDOT, + 0x0D => StyleBorder::BORDER_SLANTDASHDOT, + ]; + + /** + * Map border style + * OpenOffice documentation: 2.5.11. + * + * @param int $index + * + * @return string + */ + public static function lookup($index) + { + if (isset(self::$map[$index])) { + return self::$map[$index]; + } + + return StyleBorder::BORDER_NONE; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php new file mode 100644 index 00000000000..7b85c088330 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php @@ -0,0 +1,47 @@ + Fill::FILL_NONE, + 0x01 => Fill::FILL_SOLID, + 0x02 => Fill::FILL_PATTERN_MEDIUMGRAY, + 0x03 => Fill::FILL_PATTERN_DARKGRAY, + 0x04 => Fill::FILL_PATTERN_LIGHTGRAY, + 0x05 => Fill::FILL_PATTERN_DARKHORIZONTAL, + 0x06 => Fill::FILL_PATTERN_DARKVERTICAL, + 0x07 => Fill::FILL_PATTERN_DARKDOWN, + 0x08 => Fill::FILL_PATTERN_DARKUP, + 0x09 => Fill::FILL_PATTERN_DARKGRID, + 0x0A => Fill::FILL_PATTERN_DARKTRELLIS, + 0x0B => Fill::FILL_PATTERN_LIGHTHORIZONTAL, + 0x0C => Fill::FILL_PATTERN_LIGHTVERTICAL, + 0x0D => Fill::FILL_PATTERN_LIGHTDOWN, + 0x0E => Fill::FILL_PATTERN_LIGHTUP, + 0x0F => Fill::FILL_PATTERN_LIGHTGRID, + 0x10 => Fill::FILL_PATTERN_LIGHTTRELLIS, + 0x11 => Fill::FILL_PATTERN_GRAY125, + 0x12 => Fill::FILL_PATTERN_GRAY0625, + ]; + + /** + * Get fill pattern from index + * OpenOffice documentation: 2.5.12. + * + * @param int $index + * + * @return string + */ + public static function lookup($index) + { + if (isset(self::$map[$index])) { + return self::$map[$index]; + } + + return Fill::FILL_NONE; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php index 9b5b7db42df..124cc3b252f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Hyperlink; -use PhpOffice\PhpSpreadsheet\NamedRange; +use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart; @@ -35,6 +35,8 @@ use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use SimpleXMLElement; +use stdClass; +use Throwable; use XMLReader; use ZipArchive; @@ -69,8 +71,6 @@ class Xlsx extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return bool */ public function canRead($pFilename) @@ -95,8 +95,6 @@ class Xlsx extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetNames($pFilename) @@ -140,8 +138,6 @@ class Xlsx extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetInfo($pFilename) @@ -262,7 +258,7 @@ class Xlsx extends BaseReader return isset($c->v) ? (string) $c->v : null; } - private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType) + private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType): void { $cellDataType = 'f'; $value = "={$c->f}"; @@ -288,7 +284,6 @@ class Xlsx extends BaseReader } /** - * @param ZipArchive $archive * @param string $fileName * * @return string @@ -318,8 +313,6 @@ class Xlsx extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -509,8 +502,10 @@ class Xlsx extends BaseReader // We shouldn't override any of the built-in MS Excel values (values below id 164) // But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used // So we make allowance for them rather than lose formatting masks - if ((int) $xf['numFmtId'] < 164 && - NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '') { + if ( + (int) $xf['numFmtId'] < 164 && + NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== '' + ) { $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']); } } @@ -666,7 +661,10 @@ class Xlsx extends BaseReader $coordinates = Coordinate::coordinateFromString($r); if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) { - $rowIndex += 1; + if (isset($c->f)) { + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError'); + } + ++$rowIndex; continue; } @@ -752,9 +750,9 @@ class Xlsx extends BaseReader (int) ($c['s']) : 0); } } - $rowIndex += 1; + ++$rowIndex; } - $cIndex += 1; + ++$cIndex; } } @@ -770,13 +768,8 @@ class Xlsx extends BaseReader } } - if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) { - $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection['password'], true); - if ($xmlSheet->protectedRanges->protectedRange) { - foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) { - $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true); - } - } + if ($xmlSheet) { + $this->readSheetProtection($docSheet, $xmlSheet); } if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) { @@ -899,7 +892,7 @@ class Xlsx extends BaseReader Settings::getLibXmlLoaderOptions() ); $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml'); - } catch (\Throwable $ex) { + } catch (Throwable $ex) { //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData continue; } @@ -1007,12 +1000,13 @@ class Xlsx extends BaseReader Settings::getLibXmlLoaderOptions() ); $drawings = []; - foreach ($relsVML->Relationship as $ele) { - if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') { - $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']); + if (isset($relsVML->Relationship)) { + foreach ($relsVML->Relationship as $ele) { + if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') { + $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']); + } } } - // Fetch VML document $vmlDrawing = simplexml_load_string( $this->securityScanner->scan($this->getFromZipArchive($zip, $vmlRelationship)), @@ -1123,7 +1117,7 @@ class Xlsx extends BaseReader $xfrm = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm; /** @var SimpleXMLElement $outerShdw */ $outerShdw = $oneCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->effectLst->outerShdw; - /** @var \SimpleXMLElement $hlinkClick */ + /** @var SimpleXMLElement $hlinkClick */ $hlinkClick = $oneCellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick; $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); @@ -1286,7 +1280,7 @@ class Xlsx extends BaseReader } // Valid range? - if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') { + if ($extractedRange == '') { continue; } @@ -1354,14 +1348,9 @@ class Xlsx extends BaseReader foreach ($xmlWorkbook->definedNames->definedName as $definedName) { // Extract range $extractedRange = (string) $definedName; - if (($spos = strpos($extractedRange, '!')) !== false) { - $extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos)); - } else { - $extractedRange = str_replace('$', '', $extractedRange); - } // Valid range? - if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') { + if ($extractedRange == '') { continue; } @@ -1376,39 +1365,43 @@ class Xlsx extends BaseReader break; default: if ($mapSheetId[(int) $definedName['localSheetId']] !== null) { + $range = Worksheet::extractSheetTitle((string) $definedName, true); + $scope = $excel->getSheet($mapSheetId[(int) $definedName['localSheetId']]); if (strpos((string) $definedName, '!') !== false) { - $range = Worksheet::extractSheetTitle((string) $definedName, true); $range[0] = str_replace("''", "'", $range[0]); $range[0] = str_replace("'", '', $range[0]); - if ($worksheet = $docSheet->getParent()->getSheetByName($range[0])) { - $extractedRange = str_replace('$', '', $range[1]); - $scope = $docSheet->getParent()->getSheet($mapSheetId[(int) $definedName['localSheetId']]); - $excel->addNamedRange(new NamedRange((string) $definedName['name'], $worksheet, $extractedRange, true, $scope)); + if ($worksheet = $excel->getSheetByName($range[0])) { + $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $worksheet, $extractedRange, true, $scope)); + } else { + $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true, $scope)); } + } else { + $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true)); } } break; } } elseif (!isset($definedName['localSheetId'])) { + $definedRange = (string) $definedName; // "Global" definedNames $locatedSheet = null; - $extractedSheetName = ''; if (strpos((string) $definedName, '!') !== false) { + // Modify range, and extract the first worksheet reference + // Need to split on a comma or a space if not in quotes, and extract the first part. + $definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $definedRange); // Extract sheet name - $extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true); - $extractedSheetName = trim($extractedSheetName[0], "'"); + [$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true); + $extractedSheetName = trim($extractedSheetName, "'"); // Locate sheet $locatedSheet = $excel->getSheetByName($extractedSheetName); - - // Modify range - [$worksheetName, $extractedRange] = Worksheet::extractSheetTitle($extractedRange, true); } - if ($locatedSheet !== null) { - $excel->addNamedRange(new NamedRange((string) $definedName['name'], $locatedSheet, $extractedRange, false)); + if ($locatedSheet === null && !DefinedName::testIfFormula($definedRange)) { + $definedRange = '#REF!'; } + $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false)); } } } @@ -1564,10 +1557,9 @@ class Xlsx extends BaseReader } /** - * @param Style $docStyle - * @param SimpleXMLElement|\stdClass $style + * @param SimpleXMLElement|stdClass $style */ - private static function readStyle(Style $docStyle, $style) + private static function readStyle(Style $docStyle, $style): void { $docStyle->getNumberFormat()->setFormatCode($style->numFmt); @@ -1620,8 +1612,6 @@ class Xlsx extends BaseReader $docStyle->getFill()->setFillType($patternType); if ($style->fill->patternFill->fgColor) { $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true)); - } else { - $docStyle->getFill()->getStartColor()->setARGB('FF000000'); } if ($style->fill->patternFill->bgColor) { $docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true)); @@ -1694,10 +1684,9 @@ class Xlsx extends BaseReader } /** - * @param Border $docBorder * @param SimpleXMLElement $eleBorder */ - private static function readBorder(Border $docBorder, $eleBorder) + private static function readBorder(Border $docBorder, $eleBorder): void { if (isset($eleBorder['style'])) { $docBorder->setBorderStyle((string) $eleBorder['style']); @@ -1735,12 +1724,16 @@ class Xlsx extends BaseReader if (isset($run->rPr->color)) { $objText->getFont()->setColor(new Color(self::readColor($run->rPr->color))); } - if ((isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) || - (isset($run->rPr->b) && !isset($run->rPr->b['val']))) { + if ( + (isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) || + (isset($run->rPr->b) && !isset($run->rPr->b['val'])) + ) { $objText->getFont()->setBold(true); } - if ((isset($run->rPr->i['val']) && self::boolean((string) $run->rPr->i['val'])) || - (isset($run->rPr->i) && !isset($run->rPr->i['val']))) { + if ( + (isset($run->rPr->i['val']) && self::boolean((string) $run->rPr->i['val'])) || + (isset($run->rPr->i) && !isset($run->rPr->i['val'])) + ) { $objText->getFont()->setItalic(true); } if (isset($run->rPr->vertAlign, $run->rPr->vertAlign['val'])) { @@ -1757,8 +1750,10 @@ class Xlsx extends BaseReader } elseif (isset($run->rPr->u, $run->rPr->u['val'])) { $objText->getFont()->setUnderline((string) $run->rPr->u['val']); } - if ((isset($run->rPr->strike['val']) && self::boolean((string) $run->rPr->strike['val'])) || - (isset($run->rPr->strike) && !isset($run->rPr->strike['val']))) { + if ( + (isset($run->rPr->strike['val']) && self::boolean((string) $run->rPr->strike['val'])) || + (isset($run->rPr->strike) && !isset($run->rPr->strike['val'])) + ) { $objText->getFont()->setStrikethrough(true); } } @@ -1770,11 +1765,10 @@ class Xlsx extends BaseReader } /** - * @param Spreadsheet $excel * @param mixed $customUITarget * @param mixed $zip */ - private function readRibbon(Spreadsheet $excel, $customUITarget, $zip) + private function readRibbon(Spreadsheet $excel, $customUITarget, $zip): void { $baseDir = dirname($customUITarget); $nameCustomUI = basename($customUITarget); @@ -1828,7 +1822,7 @@ class Xlsx extends BaseReader private static function toCSSArray($style) { - $style = trim(str_replace(["\r", "\n"], '', $style), ';'); + $style = self::stripWhiteSpaceFromStyleString($style); $temp = explode(';', $style); $style = []; @@ -1857,6 +1851,11 @@ class Xlsx extends BaseReader return $style; } + public static function stripWhiteSpaceFromStyleString($string) + { + return trim(str_replace(["\r", "\n", ' '], '', $string), ';'); + } + private static function boolean($value) { if (is_object($value)) { @@ -1871,10 +1870,10 @@ class Xlsx extends BaseReader /** * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing - * @param \SimpleXMLElement $cellAnchor + * @param SimpleXMLElement $cellAnchor * @param array $hyperlinks */ - private function readHyperLinkDrawing($objDrawing, $cellAnchor, $hyperlinks) + private function readHyperLinkDrawing($objDrawing, $cellAnchor, $hyperlinks): void { $hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->hlinkClick; @@ -1890,7 +1889,7 @@ class Xlsx extends BaseReader $objDrawing->setHyperlink($hyperlink); } - private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook) + private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook): void { if (!$xmlWorkbook->workbookProtection) { return; @@ -1917,7 +1916,7 @@ class Xlsx extends BaseReader } } - private function readFormControlProperties(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData) + private function readFormControlProperties(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void { if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) { return; @@ -1949,7 +1948,7 @@ class Xlsx extends BaseReader unset($unparsedCtrlProps); } - private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData) + private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void { if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) { return; @@ -1972,7 +1971,7 @@ class Xlsx extends BaseReader $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings']; foreach ($sheetPrinterSettings as $rId => $printerSettings) { - $rId = substr($rId, 3); // rIdXXX + $rId = substr($rId, 3) . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing $unparsedPrinterSettings[$rId] = []; $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']); $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target']; @@ -2037,4 +2036,29 @@ class Xlsx extends BaseReader return $workbookBasename; } + + private function readSheetProtection(Worksheet $docSheet, SimpleXMLElement $xmlSheet): void + { + if ($this->readDataOnly || !$xmlSheet->sheetProtection) { + return; + } + + $algorithmName = (string) $xmlSheet->sheetProtection['algorithmName']; + $protection = $docSheet->getProtection(); + $protection->setAlgorithm($algorithmName); + + if ($algorithmName) { + $protection->setPassword((string) $xmlSheet->sheetProtection['hashValue'], true); + $protection->setSalt((string) $xmlSheet->sheetProtection['saltValue']); + $protection->setSpinCount((int) $xmlSheet->sheetProtection['spinCount']); + } else { + $protection->setPassword((string) $xmlSheet->sheetProtection['password'], true); + } + + if ($xmlSheet->protectedRanges->protectedRange) { + foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) { + $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true); + } + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php index 6929758dfe9..f52bfd41a8c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class AutoFilter { @@ -12,21 +13,22 @@ class AutoFilter private $worksheetXml; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml) + public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; } - public function load() + public function load(): void { - $autoFilterRange = (string) $this->worksheetXml->autoFilter['ref']; + // Remove all "$" in the auto filter range + $autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']); if (strpos($autoFilterRange, ':') !== false) { $this->readAutoFilter($autoFilterRange, $this->worksheetXml); } } - private function readAutoFilter($autoFilterRange, $xmlSheet) + private function readAutoFilter($autoFilterRange, $xmlSheet): void { $autoFilter = $this->worksheet->getAutoFilter(); $autoFilter->setRange($autoFilterRange); @@ -61,7 +63,7 @@ class AutoFilter } } - private function readDateRangeAutoFilter(\SimpleXMLElement $filters, Column $column) + private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void { foreach ($filters->dateGroupItem as $dateGroupItem) { // Operator is undefined, but always treated as EQUAL @@ -80,7 +82,7 @@ class AutoFilter } } - private function readCustomAutoFilter(\SimpleXMLElement $filterColumn, Column $column) + private function readCustomAutoFilter(SimpleXMLElement $filterColumn, Column $column): void { if ($filterColumn->customFilters) { $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); @@ -99,7 +101,7 @@ class AutoFilter } } - private function readDynamicAutoFilter(\SimpleXMLElement $filterColumn, Column $column) + private function readDynamicAutoFilter(SimpleXMLElement $filterColumn, Column $column): void { if ($filterColumn->dynamicFilter) { $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER); @@ -121,7 +123,7 @@ class AutoFilter } } - private function readTopTenAutoFilter(\SimpleXMLElement $filterColumn, Column $column) + private function readTopTenAutoFilter(SimpleXMLElement $filterColumn, Column $column): void { if ($filterColumn->top10) { $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 2b920d705c3..c9a230c215d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -17,7 +17,6 @@ use SimpleXMLElement; class Chart { /** - * @param SimpleXMLElement $component * @param string $name * @param string $format * @@ -51,7 +50,6 @@ class Chart } /** - * @param SimpleXMLElement $chartElements * @param string $chartName * * @return \PhpOffice\PhpSpreadsheet\Chart\Chart @@ -529,10 +527,9 @@ class Chart } /** - * @param Layout $plotArea * @param mixed $plotAttributes */ - private static function setChartAttributes(Layout $plotArea, $plotAttributes) + private static function setChartAttributes(Layout $plotArea, $plotAttributes): void { foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) { switch ($plotAttributeKey) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php index e901d99048e..4134b2f1843 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class ColumnAndRowAttributes extends BaseParserClass { @@ -12,7 +13,7 @@ class ColumnAndRowAttributes extends BaseParserClass private $worksheetXml; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null) + public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; @@ -25,7 +26,7 @@ class ColumnAndRowAttributes extends BaseParserClass * @param array $columnAttributes array of attributes (indexes are attribute name, values are value) * 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ? */ - private function setColumnAttributes($columnAddress, array $columnAttributes) + private function setColumnAttributes($columnAddress, array $columnAttributes): void { if (isset($columnAttributes['xfIndex'])) { $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']); @@ -51,7 +52,7 @@ class ColumnAndRowAttributes extends BaseParserClass * @param array $rowAttributes array of attributes (indexes are attribute name, values are value) * 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ? */ - private function setRowAttributes($rowNumber, array $rowAttributes) + private function setRowAttributes($rowNumber, array $rowAttributes): void { if (isset($rowAttributes['xfIndex'])) { $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']); @@ -74,7 +75,7 @@ class ColumnAndRowAttributes extends BaseParserClass * @param IReadFilter $readFilter * @param bool $readDataOnly */ - public function load(IReadFilter $readFilter = null, $readDataOnly = false) + public function load(?IReadFilter $readFilter = null, $readDataOnly = false): void { if ($this->worksheetXml === null) { return; @@ -93,8 +94,10 @@ class ColumnAndRowAttributes extends BaseParserClass // set columns/rows attributes $columnsAttributesAreSet = []; foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) { - if ($readFilter === null || - !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)) { + if ( + $readFilter === null || + !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes) + ) { if (!isset($columnsAttributesAreSet[$columnCoordinate])) { $this->setColumnAttributes($columnCoordinate, $columnAttributes); $columnsAttributesAreSet[$columnCoordinate] = true; @@ -104,8 +107,10 @@ class ColumnAndRowAttributes extends BaseParserClass $rowsAttributesAreSet = []; foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) { - if ($readFilter === null || - !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)) { + if ( + $readFilter === null || + !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes) + ) { if (!isset($rowsAttributesAreSet[$rowCoordinate])) { $this->setRowAttributes($rowCoordinate, $rowAttributes); $rowsAttributesAreSet[$rowCoordinate] = true; @@ -125,7 +130,7 @@ class ColumnAndRowAttributes extends BaseParserClass return false; } - private function readColumnAttributes(\SimpleXMLElement $worksheetCols, $readDataOnly) + private function readColumnAttributes(SimpleXMLElement $worksheetCols, $readDataOnly) { $columnAttributes = []; @@ -145,7 +150,7 @@ class ColumnAndRowAttributes extends BaseParserClass return $columnAttributes; } - private function readColumnRangeAttributes(\SimpleXMLElement $column, $readDataOnly) + private function readColumnRangeAttributes(SimpleXMLElement $column, $readDataOnly) { $columnAttributes = []; @@ -177,7 +182,7 @@ class ColumnAndRowAttributes extends BaseParserClass return false; } - private function readRowAttributes(\SimpleXMLElement $worksheetRow, $readDataOnly) + private function readRowAttributes(SimpleXMLElement $worksheetRow, $readDataOnly) { $rowAttributes = []; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index b3de5d1cde6..4aa48e17cef 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class ConditionalStyles { @@ -13,14 +14,14 @@ class ConditionalStyles private $dxfs; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml, array $dxfs = []) + public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml, array $dxfs = []) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; $this->dxfs = $dxfs; } - public function load() + public function load(): void { $this->setConditionalStyles( $this->worksheet, @@ -33,11 +34,15 @@ class ConditionalStyles $conditionals = []; foreach ($xmlSheet->conditionalFormatting as $conditional) { foreach ($conditional->cfRule as $cfRule) { - if (((string) $cfRule['type'] == Conditional::CONDITION_NONE + if ( + ((string) $cfRule['type'] == Conditional::CONDITION_NONE || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT + || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS + || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) - && isset($this->dxfs[(int) ($cfRule['dxfId'])])) { + && isset($this->dxfs[(int) ($cfRule['dxfId'])]) + ) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; } } @@ -46,7 +51,7 @@ class ConditionalStyles return $conditionals; } - private function setConditionalStyles(Worksheet $worksheet, array $conditionals) + private function setConditionalStyles(Worksheet $worksheet, array $conditionals): void { foreach ($conditionals as $ref => $cfRules) { ksort($cfRules); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php index 4bb44129208..41a8c9fb57c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class DataValidations { @@ -11,13 +12,13 @@ class DataValidations private $worksheetXml; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml) + public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; } - public function load() + public function load(): void { foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) { // Uppercase coordinate diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php index 400b272528c..106fd44efc1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class Hyperlinks { @@ -16,7 +17,7 @@ class Hyperlinks $this->worksheet = $workSheet; } - public function readHyperlinks(\SimpleXMLElement $relsWorksheet) + public function readHyperlinks(SimpleXMLElement $relsWorksheet): void { foreach ($relsWorksheet->Relationship as $element) { if ($element['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') { @@ -25,14 +26,14 @@ class Hyperlinks } } - public function setHyperlinks(\SimpleXMLElement $worksheetXml) + public function setHyperlinks(SimpleXMLElement $worksheetXml): void { foreach ($worksheetXml->hyperlink as $hyperlink) { $this->setHyperlink($hyperlink, $this->worksheet); } } - private function setHyperlink(\SimpleXMLElement $hyperlink, Worksheet $worksheet) + private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void { // Link url $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'); @@ -40,7 +41,7 @@ class Hyperlinks foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) { $cell = $worksheet->getCell($cellReference); if (isset($linkRel['id'])) { - $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']]; + $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']] ?? null; if (isset($hyperlink['location'])) { $hyperlinkUrl .= '#' . (string) $hyperlink['location']; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php index 6f286769c0e..536c8948d85 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class PageSetup extends BaseParserClass { @@ -11,7 +12,7 @@ class PageSetup extends BaseParserClass private $worksheetXml; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null) + public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; @@ -31,7 +32,7 @@ class PageSetup extends BaseParserClass return $unparsedLoadedData; } - private function margins(\SimpleXMLElement $xmlSheet, Worksheet $worksheet) + private function margins(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void { if ($xmlSheet->pageMargins) { $docPageMargins = $worksheet->getPageMargins(); @@ -44,7 +45,7 @@ class PageSetup extends BaseParserClass } } - private function pageSetup(\SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData) + private function pageSetup(SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData) { if ($xmlSheet->pageSetup) { $docPageSetup = $worksheet->getPageSetup(); @@ -64,10 +65,15 @@ class PageSetup extends BaseParserClass if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) { $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false); } - if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) && - self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) { + if ( + isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) && + self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber']) + ) { $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber'])); } + if (isset($xmlSheet->pageSetup['pageOrder'])) { + $docPageSetup->setPageOrder((string) $xmlSheet->pageSetup['pageOrder']); + } $relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'); if (isset($relAttributes['id'])) { @@ -78,31 +84,39 @@ class PageSetup extends BaseParserClass return $unparsedLoadedData; } - private function headerFooter(\SimpleXMLElement $xmlSheet, Worksheet $worksheet) + private function headerFooter(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void { if ($xmlSheet->headerFooter) { $docHeaderFooter = $worksheet->getHeaderFooter(); - if (isset($xmlSheet->headerFooter['differentOddEven']) && - self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) { + if ( + isset($xmlSheet->headerFooter['differentOddEven']) && + self::boolean((string) $xmlSheet->headerFooter['differentOddEven']) + ) { $docHeaderFooter->setDifferentOddEven(true); } else { $docHeaderFooter->setDifferentOddEven(false); } - if (isset($xmlSheet->headerFooter['differentFirst']) && - self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) { + if ( + isset($xmlSheet->headerFooter['differentFirst']) && + self::boolean((string) $xmlSheet->headerFooter['differentFirst']) + ) { $docHeaderFooter->setDifferentFirst(true); } else { $docHeaderFooter->setDifferentFirst(false); } - if (isset($xmlSheet->headerFooter['scaleWithDoc']) && - !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) { + if ( + isset($xmlSheet->headerFooter['scaleWithDoc']) && + !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc']) + ) { $docHeaderFooter->setScaleWithDocument(false); } else { $docHeaderFooter->setScaleWithDocument(true); } - if (isset($xmlSheet->headerFooter['alignWithMargins']) && - !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) { + if ( + isset($xmlSheet->headerFooter['alignWithMargins']) && + !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins']) + ) { $docHeaderFooter->setAlignWithMargins(false); } else { $docHeaderFooter->setAlignWithMargins(true); @@ -117,7 +131,7 @@ class PageSetup extends BaseParserClass } } - private function pageBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet) + private function pageBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void { if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) { $this->rowBreaks($xmlSheet, $worksheet); @@ -127,7 +141,7 @@ class PageSetup extends BaseParserClass } } - private function rowBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet) + private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void { foreach ($xmlSheet->rowBreaks->brk as $brk) { if ($brk['man']) { @@ -136,7 +150,7 @@ class PageSetup extends BaseParserClass } } - private function columnBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet) + private function columnBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void { foreach ($xmlSheet->colBreaks->brk as $brk) { if ($brk['man']) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php index bf8e57d807a..b6f3c61fc3a 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\Settings; +use SimpleXMLElement; class Properties { @@ -27,7 +28,7 @@ class Properties ); } - public function readCoreProperties($propertyData) + public function readCoreProperties($propertyData): void { $xmlCore = $this->extractPropertyData($propertyData); @@ -48,7 +49,7 @@ class Properties } } - public function readExtendedProperties($propertyData) + public function readExtendedProperties($propertyData): void { $xmlCore = $this->extractPropertyData($propertyData); @@ -62,13 +63,13 @@ class Properties } } - public function readCustomProperties($propertyData) + public function readCustomProperties($propertyData): void { $xmlCore = $this->extractPropertyData($propertyData); if (is_object($xmlCore)) { foreach ($xmlCore as $xmlProperty) { - /** @var \SimpleXMLElement $xmlProperty */ + /** @var SimpleXMLElement $xmlProperty */ $cellDataOfficeAttributes = $xmlProperty->attributes(); if (isset($cellDataOfficeAttributes['name'])) { $propertyName = (string) $cellDataOfficeAttributes['name']; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php index eb61a5d306c..123588182be 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class SheetViewOptions extends BaseParserClass { @@ -10,7 +11,7 @@ class SheetViewOptions extends BaseParserClass private $worksheetXml; - public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null) + public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null) { $this->worksheet = $workSheet; $this->worksheetXml = $worksheetXml; @@ -19,7 +20,7 @@ class SheetViewOptions extends BaseParserClass /** * @param bool $readDataOnly */ - public function load($readDataOnly = false) + public function load($readDataOnly = false): void { if ($this->worksheetXml === null) { return; @@ -41,32 +42,36 @@ class SheetViewOptions extends BaseParserClass } } - private function tabColor(\SimpleXMLElement $sheetPr) + private function tabColor(SimpleXMLElement $sheetPr): void { if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) { $this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']); } } - private function codeName(\SimpleXMLElement $sheetPr) + private function codeName(SimpleXMLElement $sheetPr): void { if (isset($sheetPr['codeName'])) { $this->worksheet->setCodeName((string) $sheetPr['codeName'], false); } } - private function outlines(\SimpleXMLElement $sheetPr) + private function outlines(SimpleXMLElement $sheetPr): void { if (isset($sheetPr->outlinePr)) { - if (isset($sheetPr->outlinePr['summaryRight']) && - !self::boolean((string) $sheetPr->outlinePr['summaryRight'])) { + if ( + isset($sheetPr->outlinePr['summaryRight']) && + !self::boolean((string) $sheetPr->outlinePr['summaryRight']) + ) { $this->worksheet->setShowSummaryRight(false); } else { $this->worksheet->setShowSummaryRight(true); } - if (isset($sheetPr->outlinePr['summaryBelow']) && - !self::boolean((string) $sheetPr->outlinePr['summaryBelow'])) { + if ( + isset($sheetPr->outlinePr['summaryBelow']) && + !self::boolean((string) $sheetPr->outlinePr['summaryBelow']) + ) { $this->worksheet->setShowSummaryBelow(false); } else { $this->worksheet->setShowSummaryBelow(true); @@ -74,11 +79,13 @@ class SheetViewOptions extends BaseParserClass } } - private function pageSetup(\SimpleXMLElement $sheetPr) + private function pageSetup(SimpleXMLElement $sheetPr): void { if (isset($sheetPr->pageSetUpPr)) { - if (isset($sheetPr->pageSetUpPr['fitToPage']) && - !self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])) { + if ( + isset($sheetPr->pageSetUpPr['fitToPage']) && + !self::boolean((string) $sheetPr->pageSetUpPr['fitToPage']) + ) { $this->worksheet->getPageSetup()->setFitToPage(false); } else { $this->worksheet->getPageSetup()->setFitToPage(true); @@ -86,11 +93,13 @@ class SheetViewOptions extends BaseParserClass } } - private function sheetFormat(\SimpleXMLElement $sheetFormatPr) + private function sheetFormat(SimpleXMLElement $sheetFormatPr): void { - if (isset($sheetFormatPr['customHeight']) && + if ( + isset($sheetFormatPr['customHeight']) && self::boolean((string) $sheetFormatPr['customHeight']) && - isset($sheetFormatPr['defaultRowHeight'])) { + isset($sheetFormatPr['defaultRowHeight']) + ) { $this->worksheet->getDefaultRowDimension() ->setRowHeight((float) $sheetFormatPr['defaultRowHeight']); } @@ -100,13 +109,15 @@ class SheetViewOptions extends BaseParserClass ->setWidth((float) $sheetFormatPr['defaultColWidth']); } - if (isset($sheetFormatPr['zeroHeight']) && - ((string) $sheetFormatPr['zeroHeight'] === '1')) { + if ( + isset($sheetFormatPr['zeroHeight']) && + ((string) $sheetFormatPr['zeroHeight'] === '1') + ) { $this->worksheet->getDefaultRowDimension()->setZeroHeight(true); } } - private function printOptions(\SimpleXMLElement $printOptions) + private function printOptions(SimpleXMLElement $printOptions): void { if (self::boolean((string) $printOptions['gridLinesSet'])) { $this->worksheet->setShowGridlines(true); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php index 88c01ead373..f6c47929067 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use SimpleXMLElement; class SheetViews extends BaseParserClass { @@ -11,13 +12,13 @@ class SheetViews extends BaseParserClass private $worksheet; - public function __construct(\SimpleXMLElement $sheetViewXml, Worksheet $workSheet) + public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet) { $this->sheetViewXml = $sheetViewXml; $this->worksheet = $workSheet; } - public function load() + public function load(): void { $this->zoomScale(); $this->view(); @@ -34,7 +35,7 @@ class SheetViews extends BaseParserClass } } - private function zoomScale() + private function zoomScale(): void { if (isset($this->sheetViewXml['zoomScale'])) { $zoomScale = (int) ($this->sheetViewXml['zoomScale']); @@ -59,14 +60,14 @@ class SheetViews extends BaseParserClass } } - private function view() + private function view(): void { if (isset($this->sheetViewXml['view'])) { $this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']); } } - private function gridLines() + private function gridLines(): void { if (isset($this->sheetViewXml['showGridLines'])) { $this->worksheet->setShowGridLines( @@ -75,7 +76,7 @@ class SheetViews extends BaseParserClass } } - private function headers() + private function headers(): void { if (isset($this->sheetViewXml['showRowColHeaders'])) { $this->worksheet->setShowRowColHeaders( @@ -84,7 +85,7 @@ class SheetViews extends BaseParserClass } } - private function direction() + private function direction(): void { if (isset($this->sheetViewXml['rightToLeft'])) { $this->worksheet->setRightToLeft( @@ -93,7 +94,7 @@ class SheetViews extends BaseParserClass } } - private function showZeros() + private function showZeros(): void { if (isset($this->sheetViewXml['showZeros'])) { $this->worksheet->getSheetView()->setShowZeros( @@ -102,7 +103,7 @@ class SheetViews extends BaseParserClass } } - private function pane() + private function pane(): void { $xSplit = 0; $ySplit = 0; @@ -126,7 +127,7 @@ class SheetViews extends BaseParserClass ); } - private function selection() + private function selection(): void { $sqref = (string) $this->sheetViewXml->selection['sqref']; $sqref = explode(' ', $sqref); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php index c98780b02d3..43de87875e1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php @@ -8,8 +8,10 @@ use PhpOffice\PhpSpreadsheet\Style\Borders; use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; +use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Style\Style; +use SimpleXMLElement; class Styles extends BaseParserClass { @@ -26,19 +28,19 @@ class Styles extends BaseParserClass private $styleXml; - public function __construct(\SimpleXMLElement $styleXml) + public function __construct(SimpleXMLElement $styleXml) { $this->styleXml = $styleXml; } - public function setStyleBaseData(Theme $theme = null, $styles = [], $cellStyles = []) + public function setStyleBaseData(?Theme $theme = null, $styles = [], $cellStyles = []): void { self::$theme = $theme; $this->styles = $styles; $this->cellStyles = $cellStyles; } - private static function readFontStyle(Font $fontStyle, \SimpleXMLElement $fontStyleXml) + private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void { $fontStyle->setName((string) $fontStyleXml->name['val']); $fontStyle->setSize((float) $fontStyleXml->sz['val']); @@ -71,10 +73,21 @@ class Styles extends BaseParserClass } } - private static function readFillStyle(Fill $fillStyle, \SimpleXMLElement $fillStyleXml) + private static function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void + { + if ($numfmtStyleXml->count() === 0) { + return; + } + $numfmt = $numfmtStyleXml->attributes(); + if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) { + $numfmtStyle->setFormatCode((string) $numfmt['formatCode']); + } + } + + private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void { if ($fillStyleXml->gradientFill) { - /** @var \SimpleXMLElement $gradientFill */ + /** @var SimpleXMLElement $gradientFill */ $gradientFill = $fillStyleXml->gradientFill[0]; if (!empty($gradientFill['type'])) { $fillStyle->setFillType((string) $gradientFill['type']); @@ -97,7 +110,7 @@ class Styles extends BaseParserClass } } - private static function readBorderStyle(Borders $borderStyle, \SimpleXMLElement $borderStyleXml) + private static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void { $diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']); $diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']); @@ -118,7 +131,7 @@ class Styles extends BaseParserClass self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal); } - private static function readBorder(Border $border, \SimpleXMLElement $borderXml) + private static function readBorder(Border $border, SimpleXMLElement $borderXml): void { if (isset($borderXml['style'])) { $border->setBorderStyle((string) $borderXml['style']); @@ -128,7 +141,7 @@ class Styles extends BaseParserClass } } - private static function readAlignmentStyle(Alignment $alignment, \SimpleXMLElement $alignmentXml) + private static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void { $alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']); $alignment->setVertical((string) $alignmentXml->alignment['vertical']); @@ -147,9 +160,13 @@ class Styles extends BaseParserClass $alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0); } - private function readStyle(Style $docStyle, $style) + private function readStyle(Style $docStyle, $style): void { - $docStyle->getNumberFormat()->setFormatCode($style->numFmt); + if ($style->numFmt instanceof SimpleXMLElement) { + self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt); + } else { + $docStyle->getNumberFormat()->setFormatCode($style->numFmt); + } if (isset($style->font)) { self::readFontStyle($docStyle->getFont(), $style->font); @@ -179,7 +196,7 @@ class Styles extends BaseParserClass } } - private function readProtectionLocked(Style $docStyle, $style) + private function readProtectionLocked(Style $docStyle, $style): void { if (isset($style->protection['locked'])) { if (self::boolean((string) $style->protection['locked'])) { @@ -190,7 +207,7 @@ class Styles extends BaseParserClass } } - private function readProtectionHidden(Style $docStyle, $style) + private function readProtectionHidden(Style $docStyle, $style): void { if (isset($style->protection['hidden'])) { if (self::boolean((string) $style->protection['hidden'])) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php index b4fffa4fb8b..11aa1df3bdf 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php @@ -2,10 +2,13 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use PhpOffice\PhpSpreadsheet\Cell\AddressHelper; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; +use PhpOffice\PhpSpreadsheet\Reader\Xml\PageSettings; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Settings; use PhpOffice\PhpSpreadsheet\Shared\Date; @@ -14,6 +17,8 @@ use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; +use PhpOffice\PhpSpreadsheet\Style\Borders; +use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; use SimpleXMLElement; @@ -29,13 +34,6 @@ class Xml extends BaseReader */ protected $styles = []; - /** - * Character set used in the file. - * - * @var string - */ - protected $charSet = 'UTF-8'; - /** * Create a new Excel2003XML Reader instance. */ @@ -45,13 +43,61 @@ class Xml extends BaseReader $this->securityScanner = XmlScanner::getInstance($this); } + private $fileContents = ''; + + private static $mappings = [ + 'borderStyle' => [ + '1continuous' => Border::BORDER_THIN, + '1dash' => Border::BORDER_DASHED, + '1dashdot' => Border::BORDER_DASHDOT, + '1dashdotdot' => Border::BORDER_DASHDOTDOT, + '1dot' => Border::BORDER_DOTTED, + '1double' => Border::BORDER_DOUBLE, + '2continuous' => Border::BORDER_MEDIUM, + '2dash' => Border::BORDER_MEDIUMDASHED, + '2dashdot' => Border::BORDER_MEDIUMDASHDOT, + '2dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT, + '2dot' => Border::BORDER_DOTTED, + '2double' => Border::BORDER_DOUBLE, + '3continuous' => Border::BORDER_THICK, + '3dash' => Border::BORDER_MEDIUMDASHED, + '3dashdot' => Border::BORDER_MEDIUMDASHDOT, + '3dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT, + '3dot' => Border::BORDER_DOTTED, + '3double' => Border::BORDER_DOUBLE, + ], + 'fillType' => [ + 'solid' => Fill::FILL_SOLID, + 'gray75' => Fill::FILL_PATTERN_DARKGRAY, + 'gray50' => Fill::FILL_PATTERN_MEDIUMGRAY, + 'gray25' => Fill::FILL_PATTERN_LIGHTGRAY, + 'gray125' => Fill::FILL_PATTERN_GRAY125, + 'gray0625' => Fill::FILL_PATTERN_GRAY0625, + 'horzstripe' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe + 'vertstripe' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe + 'reversediagstripe' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe + 'diagstripe' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe + 'diagcross' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch + 'thickdiagcross' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch + 'thinhorzstripe' => Fill::FILL_PATTERN_LIGHTHORIZONTAL, + 'thinvertstripe' => Fill::FILL_PATTERN_LIGHTVERTICAL, + 'thinreversediagstripe' => Fill::FILL_PATTERN_LIGHTUP, + 'thindiagstripe' => Fill::FILL_PATTERN_LIGHTDOWN, + 'thinhorzcross' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch + 'thindiagcross' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch + ], + ]; + + public static function xmlMappings(): array + { + return self::$mappings; + } + /** * Can the current IReader read the file? * * @param string $pFilename * - * @throws Exception - * * @return bool */ public function canRead($pFilename) @@ -72,13 +118,10 @@ class Xml extends BaseReader ]; // Open file - $this->openFile($pFilename); - $fileHandle = $this->fileHandle; + $data = file_get_contents($pFilename); - // Read sample data (first 2 KB will do) - $data = fread($fileHandle, 2048); - fclose($fileHandle); - $data = str_replace("'", '"', $data); // fix headers with single quote + // Why? + //$data = str_replace("'", '"', $data); // fix headers with single quote $valid = true; foreach ($signature as $match) { @@ -91,9 +134,14 @@ class Xml extends BaseReader } // Retrieve charset encoding - if (preg_match('//um', $data, $matches)) { - $this->charSet = strtoupper($matches[1]); + if (preg_match('//m', $data, $matches)) { + $charSet = strtoupper($matches[1]); + if (1 == preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet)) { + $data = StringHelper::convertEncoding($data, 'UTF-8', $charSet); + $data = preg_replace('/()/um', '$1' . 'UTF-8' . '$2', $data, 1); + } } + $this->fileContents = $data; return $valid; } @@ -103,21 +151,20 @@ class Xml extends BaseReader * * @param string $pFilename * - * @throws Exception - * - * @return false|\SimpleXMLElement + * @return false|SimpleXMLElement */ public function trySimpleXMLLoadString($pFilename) { try { $xml = simplexml_load_string( - $this->securityScanner->scan(file_get_contents($pFilename)), + $this->securityScanner->scan($this->fileContents ?: file_get_contents($pFilename)), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions() ); } catch (\Exception $e) { throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e); } + $this->fileContents = ''; return $xml; } @@ -127,8 +174,6 @@ class Xml extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetNames($pFilename) @@ -147,7 +192,7 @@ class Xml extends BaseReader $xml_ss = $xml->children($namespaces['ss']); foreach ($xml_ss->Worksheet as $worksheet) { $worksheet_ss = $worksheet->attributes($namespaces['ss']); - $worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet); + $worksheetNames[] = (string) $worksheet_ss['Name']; } return $worksheetNames; @@ -158,13 +203,14 @@ class Xml extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return array */ public function listWorksheetInfo($pFilename) { File::assertFile($pFilename); + if (!$this->canRead($pFilename)) { + throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); + } $worksheetInfo = []; @@ -184,10 +230,9 @@ class Xml extends BaseReader $tmpInfo['totalRows'] = 0; $tmpInfo['totalColumns'] = 0; + $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}"; if (isset($worksheet_ss['Name'])) { $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name']; - } else { - $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}"; } if (isset($worksheet->Table->Row)) { @@ -229,8 +274,6 @@ class Xml extends BaseReader * * @param string $pFilename * - * @throws Exception - * * @return Spreadsheet */ public function load($pFilename) @@ -245,62 +288,29 @@ class Xml extends BaseReader private static function identifyFixedStyleValue($styleList, &$styleAttributeValue) { + $returnValue = false; $styleAttributeValue = strtolower($styleAttributeValue); foreach ($styleList as $style) { if ($styleAttributeValue == strtolower($style)) { $styleAttributeValue = $style; + $returnValue = true; - return true; + break; } } - return false; - } - - /** - * pixel units to excel width units(units of 1/256th of a character width). - * - * @param float $pxs - * - * @return float - */ - protected static function pixel2WidthUnits($pxs) - { - $UNIT_OFFSET_MAP = [0, 36, 73, 109, 146, 182, 219]; - - $widthUnits = 256 * ($pxs / 7); - $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)]; - - return $widthUnits; - } - - /** - * excel width units(units of 1/256th of a character width) to pixel units. - * - * @param float $widthUnits - * - * @return float - */ - protected static function widthUnits2Pixel($widthUnits) - { - $pixels = ($widthUnits / 256) * 7; - $offsetWidthUnits = $widthUnits % 256; - - return $pixels + round($offsetWidthUnits / (256 / 7)); + return $returnValue; } protected static function hex2str($hex) { - return chr(hexdec($hex[1])); + return mb_chr((int) hexdec($hex[1]), 'UTF-8'); } /** * Loads from file into Spreadsheet instance. * * @param string $pFilename - * @param Spreadsheet $spreadsheet - * - * @throws Exception * * @return Spreadsheet */ @@ -318,51 +328,52 @@ class Xml extends BaseReader $docProps = $spreadsheet->getProperties(); if (isset($xml->DocumentProperties[0])) { foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) { + $stringValue = (string) $propertyValue; switch ($propertyName) { case 'Title': - $docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setTitle($stringValue); break; case 'Subject': - $docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setSubject($stringValue); break; case 'Author': - $docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setCreator($stringValue); break; case 'Created': - $creationDate = strtotime($propertyValue); + $creationDate = strtotime($stringValue); $docProps->setCreated($creationDate); break; case 'LastAuthor': - $docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setLastModifiedBy($stringValue); break; case 'LastSaved': - $lastSaveDate = strtotime($propertyValue); + $lastSaveDate = strtotime($stringValue); $docProps->setModified($lastSaveDate); break; case 'Company': - $docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setCompany($stringValue); break; case 'Category': - $docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setCategory($stringValue); break; case 'Manager': - $docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setManager($stringValue); break; case 'Keywords': - $docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setKeywords($stringValue); break; case 'Description': - $docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet)); + $docProps->setDescription($stringValue); break; } @@ -371,7 +382,7 @@ class Xml extends BaseReader if (isset($xml->CustomDocumentProperties)) { foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) { $propertyAttributes = $propertyValue->attributes($namespaces['dt']); - $propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', ['self', 'hex2str'], $propertyName); + $propertyName = preg_replace_callback('/_x([0-9a-f]{4})_/i', ['self', 'hex2str'], $propertyName); $propertyType = Properties::PROPERTY_TYPE_UNKNOWN; switch ((string) $propertyAttributes) { case 'string': @@ -412,8 +423,10 @@ class Xml extends BaseReader foreach ($xml_ss->Worksheet as $worksheet) { $worksheet_ss = $worksheet->attributes($namespaces['ss']); - if ((isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) && - (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))) { + if ( + (isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) && + (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly)) + ) { continue; } @@ -421,13 +434,27 @@ class Xml extends BaseReader $spreadsheet->createSheet(); $spreadsheet->setActiveSheetIndex($worksheetID); if (isset($worksheet_ss['Name'])) { - $worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet); + $worksheetName = (string) $worksheet_ss['Name']; // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in // formula cells... during the load, all formulae should be correct, and we're simply bringing // the worksheet name in line with the formula, not the reverse $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); } + // locally scoped defined names + if (isset($worksheet->Names[0])) { + foreach ($worksheet->Names[0] as $definedName) { + $definedName_ss = $definedName->attributes($namespaces['ss']); + $name = (string) $definedName_ss['Name']; + $definedValue = (string) $definedName_ss['RefersTo']; + $convertedValue = AddressHelper::convertFormulaToA1($definedValue); + if ($convertedValue[0] === '=') { + $convertedValue = substr($convertedValue, 1); + } + $spreadsheet->addDefinedName(DefinedName::createInstance($name, $spreadsheet->getActiveSheet(), $convertedValue, true)); + } + } + $columnID = 'A'; if (isset($worksheet->Table->Column)) { foreach ($worksheet->Table->Column as $columnData) { @@ -470,7 +497,7 @@ class Xml extends BaseReader } if (isset($cell_ss['HRef'])) { - $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl($cell_ss['HRef']); + $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl((string) $cell_ss['HRef']); } if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) { @@ -487,14 +514,15 @@ class Xml extends BaseReader $spreadsheet->getActiveSheet()->mergeCells($cellRange); } - $cellIsSet = $hasCalculatedValue = false; + $hasCalculatedValue = false; $cellDataFormula = ''; if (isset($cell_ss['Formula'])) { $cellDataFormula = $cell_ss['Formula']; $hasCalculatedValue = true; } if (isset($cell->Data)) { - $cellValue = $cellData = $cell->Data; + $cellData = $cell->Data; + $cellValue = (string) $cellData; $type = DataType::TYPE_NULL; $cellData_ss = $cellData->attributes($namespaces['ss']); if (isset($cellData_ss['Type'])) { @@ -510,7 +538,6 @@ class Xml extends BaseReader const TYPE_ERROR = 'e'; */ case 'String': - $cellValue = self::convertStringEncoding($cellValue, $this->charSet); $type = DataType::TYPE_STRING; break; @@ -529,11 +556,12 @@ class Xml extends BaseReader break; case 'DateTime': $type = DataType::TYPE_NUMERIC; - $cellValue = Date::PHPToExcel(strtotime($cellValue)); + $cellValue = Date::PHPToExcel(strtotime($cellValue . ' UTC')); break; case 'Error': $type = DataType::TYPE_ERROR; + $hasCalculatedValue = false; break; } @@ -542,65 +570,14 @@ class Xml extends BaseReader if ($hasCalculatedValue) { $type = DataType::TYPE_FORMULA; $columnNumber = Coordinate::columnIndexFromString($columnID); - if (substr($cellDataFormula, 0, 3) == 'of:') { - $cellDataFormula = substr($cellDataFormula, 3); - $temp = explode('"', $cellDataFormula); - $key = false; - foreach ($temp as &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) - if ($key = !$key) { - $value = str_replace(['[.', '.', ']'], '', $value); - } - } - } else { - // Convert R1C1 style references to A1 style references (but only when not quoted) - $temp = explode('"', $cellDataFormula); - $key = false; - foreach ($temp as &$value) { - // Only replace in alternate array entries (i.e. non-quoted blocks) - if ($key = !$key) { - preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); - // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way - // through the formula from left to right. Reversing means that we work right to left.through - // the formula - $cellReferences = array_reverse($cellReferences); - // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, - // then modify the formula to use that new reference - foreach ($cellReferences as $cellReference) { - $rowReference = $cellReference[2][0]; - // Empty R reference is the current row - if ($rowReference == '') { - $rowReference = $rowID; - } - // Bracketed R references are relative to the current row - if ($rowReference[0] == '[') { - $rowReference = $rowID + trim($rowReference, '[]'); - } - $columnReference = $cellReference[4][0]; - // Empty C reference is the current column - if ($columnReference == '') { - $columnReference = $columnNumber; - } - // Bracketed C references are relative to the current column - if ($columnReference[0] == '[') { - $columnReference = $columnNumber + trim($columnReference, '[]'); - } - $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference; - $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0])); - } - } - } - } - unset($value); - // Then rebuild the formula string - $cellDataFormula = implode('"', $temp); + $cellDataFormula = AddressHelper::convertFormulaToA1($cellDataFormula, $rowID, $columnNumber); } $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type); if ($hasCalculatedValue) { $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue); } - $cellIsSet = $rowHasData = true; + $rowHasData = true; } if (isset($cell->Comment)) { @@ -611,15 +588,15 @@ class Xml extends BaseReader } $node = $cell->Comment->Data->asXML(); $annotation = strip_tags($node); - $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation)); + $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor($author)->setText($this->parseRichText($annotation)); } - if (($cellIsSet) && (isset($cell_ss['StyleID']))) { + if (isset($cell_ss['StyleID'])) { $style = (string) $cell_ss['StyleID']; if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) { - if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) { - $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null); - } + //if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) { + // $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null); + //} $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]); } } @@ -639,37 +616,46 @@ class Xml extends BaseReader ++$rowID; } + + if (isset($namespaces['x'])) { + $xmlX = $worksheet->children($namespaces['x']); + if (isset($xmlX->WorksheetOptions)) { + (new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet); + } + } } ++$worksheetID; } + // Globally scoped defined names + $activeWorksheet = $spreadsheet->setActiveSheetIndex(0); + if (isset($xml->Names[0])) { + foreach ($xml->Names[0] as $definedName) { + $definedName_ss = $definedName->attributes($namespaces['ss']); + $name = (string) $definedName_ss['Name']; + $definedValue = (string) $definedName_ss['RefersTo']; + $convertedValue = AddressHelper::convertFormulaToA1($definedValue); + if ($convertedValue[0] === '=') { + $convertedValue = substr($convertedValue, 1); + } + $spreadsheet->addDefinedName(DefinedName::createInstance($name, $activeWorksheet, $convertedValue)); + } + } + // Return return $spreadsheet; } - protected static function convertStringEncoding($string, $charset) - { - if ($charset != 'UTF-8') { - return StringHelper::convertEncoding($string, 'UTF-8', $charset); - } - - return $string; - } - protected function parseRichText($is) { $value = new RichText(); - $value->createText(self::convertStringEncoding($is, $this->charSet)); + $value->createText($is); return $value; } - /** - * @param SimpleXMLElement $xml - * @param array $namespaces - */ - private function parseStyles(SimpleXMLElement $xml, array $namespaces) + private function parseStyles(SimpleXMLElement $xml, array $namespaces): void { if (!isset($xml->Styles)) { return; @@ -709,9 +695,8 @@ class Xml extends BaseReader /** * @param string $styleID - * @param SimpleXMLElement $styleAttributes */ - private function parseStyleAlignment($styleID, SimpleXMLElement $styleAttributes) + private function parseStyleAlignment($styleID, SimpleXMLElement $styleAttributes): void { $verticalAlignmentStyles = [ Alignment::VERTICAL_BOTTOM, @@ -746,31 +731,41 @@ class Xml extends BaseReader case 'WrapText': $this->styles[$styleID]['alignment']['wrapText'] = true; + break; + case 'Rotate': + $this->styles[$styleID]['alignment']['textRotation'] = $styleAttributeValue; + break; } } } + private static $borderPositions = ['top', 'left', 'bottom', 'right']; + /** * @param $styleID - * @param SimpleXMLElement $styleData - * @param array $namespaces */ - private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces) + private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void { + $diagonalDirection = ''; + $borderPosition = ''; foreach ($styleData->Border as $borderStyle) { $borderAttributes = $borderStyle->attributes($namespaces['ss']); $thisBorder = []; + $style = (string) $borderAttributes->Weight; + $style .= strtolower((string) $borderAttributes->LineStyle); + $thisBorder['borderStyle'] = self::$mappings['borderStyle'][$style] ?? Border::BORDER_NONE; foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) { switch ($borderStyleKey) { - case 'LineStyle': - $thisBorder['borderStyle'] = Border::BORDER_MEDIUM; - - break; - case 'Weight': - break; case 'Position': - $borderPosition = strtolower($borderStyleValue); + $borderStyleValue = strtolower((string) $borderStyleValue); + if (in_array($borderStyleValue, self::$borderPositions)) { + $borderPosition = $borderStyleValue; + } elseif ($borderStyleValue == 'diagonalleft') { + $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_DOWN; + } elseif ($borderStyleValue == 'diagonalright') { + $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_UP; + } break; case 'Color': @@ -780,28 +775,45 @@ class Xml extends BaseReader break; } } - if (!empty($thisBorder)) { - if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) { - $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder; - } + if ($borderPosition) { + $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder; + } elseif ($diagonalDirection) { + $this->styles[$styleID]['borders']['diagonalDirection'] = $diagonalDirection; + $this->styles[$styleID]['borders']['diagonal'] = $thisBorder; } } } + private static $underlineStyles = [ + Font::UNDERLINE_NONE, + Font::UNDERLINE_DOUBLE, + Font::UNDERLINE_DOUBLEACCOUNTING, + Font::UNDERLINE_SINGLE, + Font::UNDERLINE_SINGLEACCOUNTING, + ]; + + private function parseStyleFontUnderline(string $styleID, string $styleAttributeValue): void + { + if (self::identifyFixedStyleValue(self::$underlineStyles, $styleAttributeValue)) { + $this->styles[$styleID]['font']['underline'] = $styleAttributeValue; + } + } + + private function parseStyleFontVerticalAlign(string $styleID, string $styleAttributeValue): void + { + if ($styleAttributeValue == 'Superscript') { + $this->styles[$styleID]['font']['superscript'] = true; + } + if ($styleAttributeValue == 'Subscript') { + $this->styles[$styleID]['font']['subscript'] = true; + } + } + /** * @param $styleID - * @param SimpleXMLElement $styleAttributes */ - private function parseStyleFont($styleID, SimpleXMLElement $styleAttributes) + private function parseStyleFont(string $styleID, SimpleXMLElement $styleAttributes): void { - $underlineStyles = [ - Font::UNDERLINE_NONE, - Font::UNDERLINE_DOUBLE, - Font::UNDERLINE_DOUBLEACCOUNTING, - Font::UNDERLINE_SINGLE, - Font::UNDERLINE_SINGLEACCOUNTING, - ]; - foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { $styleAttributeValue = (string) $styleAttributeValue; switch ($styleAttributeKey) { @@ -826,9 +838,11 @@ class Xml extends BaseReader break; case 'Underline': - if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) { - $this->styles[$styleID]['font']['underline'] = $styleAttributeValue; - } + $this->parseStyleFontUnderline($styleID, $styleAttributeValue); + + break; + case 'VerticalAlign': + $this->parseStyleFontVerticalAlign($styleID, $styleAttributeValue); break; } @@ -837,18 +851,23 @@ class Xml extends BaseReader /** * @param $styleID - * @param SimpleXMLElement $styleAttributes */ - private function parseStyleInterior($styleID, SimpleXMLElement $styleAttributes) + private function parseStyleInterior($styleID, SimpleXMLElement $styleAttributes): void { foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { switch ($styleAttributeKey) { case 'Color': - $this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1); + $this->styles[$styleID]['fill']['endColor']['rgb'] = substr($styleAttributeValue, 1); + $this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1); + + break; + case 'PatternColor': + $this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1); break; case 'Pattern': - $this->styles[$styleID]['fill']['fillType'] = strtolower($styleAttributeValue); + $lcStyleAttributeValue = strtolower((string) $styleAttributeValue); + $this->styles[$styleID]['fill']['fillType'] = self::$mappings['fillType'][$lcStyleAttributeValue] ?? Fill::FILL_NONE; break; } @@ -857,9 +876,8 @@ class Xml extends BaseReader /** * @param $styleID - * @param SimpleXMLElement $styleAttributes */ - private function parseStyleNumberFormat($styleID, SimpleXMLElement $styleAttributes) + private function parseStyleNumberFormat($styleID, SimpleXMLElement $styleAttributes): void { $fromFormats = ['\-', '\ ']; $toFormats = ['-', ' ']; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php new file mode 100644 index 00000000000..e56ac331f37 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php @@ -0,0 +1,130 @@ +pageSetup($xmlX, $namespaces, $this->getPrintDefaults()); + $this->printSettings = $this->printSetup($xmlX, $printSettings); + } + + public function loadPageSettings(Spreadsheet $spreadsheet): void + { + $spreadsheet->getActiveSheet()->getPageSetup() + ->setPaperSize($this->printSettings->paperSize) + ->setOrientation($this->printSettings->orientation) + ->setScale($this->printSettings->scale) + ->setVerticalCentered($this->printSettings->verticalCentered) + ->setHorizontalCentered($this->printSettings->horizontalCentered) + ->setPageOrder($this->printSettings->printOrder); + $spreadsheet->getActiveSheet()->getPageMargins() + ->setTop($this->printSettings->topMargin) + ->setHeader($this->printSettings->headerMargin) + ->setLeft($this->printSettings->leftMargin) + ->setRight($this->printSettings->rightMargin) + ->setBottom($this->printSettings->bottomMargin) + ->setFooter($this->printSettings->footerMargin); + } + + private function getPrintDefaults(): stdClass + { + return (object) [ + 'paperSize' => 9, + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'printOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + 'topMargin' => 0.75, + 'headerMargin' => 0.3, + 'leftMargin' => 0.7, + 'rightMargin' => 0.7, + 'bottomMargin' => 0.75, + 'footerMargin' => 0.3, + ]; + } + + private function pageSetup(SimpleXMLElement $xmlX, array $namespaces, stdClass $printDefaults): stdClass + { + if (isset($xmlX->WorksheetOptions->PageSetup)) { + foreach ($xmlX->WorksheetOptions->PageSetup as $pageSetupData) { + foreach ($pageSetupData as $pageSetupKey => $pageSetupValue) { + $pageSetupAttributes = $pageSetupValue->attributes($namespaces['x']); + switch ($pageSetupKey) { + case 'Layout': + $this->setLayout($printDefaults, $pageSetupAttributes); + + break; + case 'Header': + $printDefaults->headerMargin = (float) $pageSetupAttributes->Margin ?: 1.0; + + break; + case 'Footer': + $printDefaults->footerMargin = (float) $pageSetupAttributes->Margin ?: 1.0; + + break; + case 'PageMargins': + $this->setMargins($printDefaults, $pageSetupAttributes); + + break; + } + } + } + } + + return $printDefaults; + } + + private function printSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass + { + if (isset($xmlX->WorksheetOptions->Print)) { + foreach ($xmlX->WorksheetOptions->Print as $printData) { + foreach ($printData as $printKey => $printValue) { + switch ($printKey) { + case 'LeftToRight': + $printDefaults->printOrder = PageSetup::PAGEORDER_OVER_THEN_DOWN; + + break; + case 'PaperSizeIndex': + $printDefaults->paperSize = (int) $printValue ?: 9; + + break; + case 'Scale': + $printDefaults->scale = (int) $printValue ?: 100; + + break; + } + } + } + } + + return $printDefaults; + } + + private function setLayout(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void + { + $printDefaults->orientation = (string) strtolower($pageSetupAttributes->Orientation) ?: PageSetup::ORIENTATION_PORTRAIT; + $printDefaults->horizontalCentered = (bool) $pageSetupAttributes->CenterHorizontal ?: false; + $printDefaults->verticalCentered = (bool) $pageSetupAttributes->CenterVertical ?: false; + } + + private function setMargins(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void + { + $printDefaults->leftMargin = (float) $pageSetupAttributes->Left ?: 1.0; + $printDefaults->rightMargin = (float) $pageSetupAttributes->Right ?: 1.0; + $printDefaults->topMargin = (float) $pageSetupAttributes->Top ?: 1.0; + $printDefaults->bottomMargin = (float) $pageSetupAttributes->Bottom ?: 1.0; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php index 143e80d8ce9..13f7cf71b9b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; @@ -68,7 +69,7 @@ class ReferenceHelper */ public static function columnReverseSort($a, $b) { - return 1 - strcasecmp(strlen($a) . $a, strlen($b) . $b); + return -strcasecmp(strlen($a) . $a, strlen($b) . $b); } /** @@ -107,7 +108,7 @@ class ReferenceHelper [$bc, $br] = sscanf($b, '%[A-Z]%d'); if ($ar === $br) { - return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc); + return -strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc); } return ($ar < $br) ? 1 : -1; @@ -129,13 +130,17 @@ class ReferenceHelper [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress); $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn); // Is cell within the range of rows/columns if we're deleting - if ($pNumRows < 0 && + if ( + $pNumRows < 0 && ($cellRow >= ($beforeRow + $pNumRows)) && - ($cellRow < $beforeRow)) { + ($cellRow < $beforeRow) + ) { return true; - } elseif ($pNumCols < 0 && + } elseif ( + $pNumCols < 0 && ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) && - ($cellColumnIndex < $beforeColumnIndex)) { + ($cellColumnIndex < $beforeColumnIndex) + ) { return true; } @@ -152,7 +157,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustPageBreaks(Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustPageBreaks(Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aBreaks = $pSheet->getBreaks(); ($pNumCols > 0 || $pNumRows > 0) ? @@ -185,7 +190,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aComments = $pSheet->getComments(); $aNewComments = []; // the new array of all comments @@ -212,7 +217,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aHyperlinkCollection = $pSheet->getHyperlinkCollection(); ($pNumCols > 0 || $pNumRows > 0) ? @@ -237,7 +242,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aDataValidationCollection = $pSheet->getDataValidationCollection(); ($pNumCols > 0 || $pNumRows > 0) ? @@ -262,7 +267,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aMergeCells = $pSheet->getMergeCells(); $aNewMergeCells = []; // the new array of all merge cells @@ -283,7 +288,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aProtectedCells = $pSheet->getProtectedCells(); ($pNumCols > 0 || $pNumRows > 0) ? @@ -307,7 +312,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true); if (!empty($aColumnDimensions)) { @@ -332,7 +337,7 @@ class ReferenceHelper * @param int $beforeRow Number of the row we're inserting/deleting before * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) */ - protected function adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows) + protected function adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows): void { $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true); if (!empty($aRowDimensions)) { @@ -363,10 +368,8 @@ class ReferenceHelper * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion) * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion) * @param Worksheet $pSheet The worksheet that we're editing - * - * @throws Exception */ - public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pSheet) + public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pSheet): void { $remove = ($pNumCols < 0 || $pNumRows < 0); $allCoordinates = $pSheet->getCoordinates(); @@ -602,11 +605,11 @@ class ReferenceHelper } } - // Update workbook: named ranges - if (count($pSheet->getParent()->getNamedRanges()) > 0) { - foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) { - if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) { - $namedRange->setRange($this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows)); + // Update workbook: define names + if (count($pSheet->getParent()->getDefinedNames()) > 0) { + foreach ($pSheet->getParent()->getDefinedNames() as $definedName) { + if ($definedName->getWorksheet()->getHashCode() === $pSheet->getHashCode()) { + $definedName->setValue($this->updateCellReference($definedName->getValue(), $pBefore, $pNumCols, $pNumRows)); } } } @@ -624,8 +627,6 @@ class ReferenceHelper * @param int $pNumRows Number of rows to insert * @param string $sheetName Worksheet name/title * - * @throws Exception - * * @return string Updated formula */ public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '') @@ -758,6 +759,141 @@ class ReferenceHelper return implode('"', $formulaBlocks); } + /** + * Update all cell references within a formula, irrespective of worksheet. + */ + public function updateFormulaReferencesAnyWorksheet(string $formula = '', int $insertColumns = 0, int $insertRows = 0): string + { + $formula = $this->updateCellReferencesAllWorksheets($formula, $insertColumns, $insertRows); + + if ($insertColumns !== 0) { + $formula = $this->updateColumnRangesAllWorksheets($formula, $insertColumns); + } + + if ($insertRows !== 0) { + $formula = $this->updateRowRangesAllWorksheets($formula, $insertRows); + } + + return $formula; + } + + private function updateCellReferencesAllWorksheets(string $formula, int $insertColumns, int $insertRows): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui', + $formula, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $columnLengths = array_map('strlen', array_column($splitRanges[6], 0)); + $rowLengths = array_map('strlen', array_column($splitRanges[7], 0)); + $columnOffsets = array_column($splitRanges[6], 1); + $rowOffsets = array_column($splitRanges[7], 1); + + $columns = $splitRanges[6]; + $rows = $splitRanges[7]; + + while ($splitCount > 0) { + --$splitCount; + $columnLength = $columnLengths[$splitCount]; + $rowLength = $rowLengths[$splitCount]; + $columnOffset = $columnOffsets[$splitCount]; + $rowOffset = $rowOffsets[$splitCount]; + $column = $columns[$splitCount][0]; + $row = $rows[$splitCount][0]; + + if (!empty($column) && $column[0] !== '$') { + $column = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($column) + $insertColumns); + $formula = substr($formula, 0, $columnOffset) . $column . substr($formula, $columnOffset + $columnLength); + } + if (!empty($row) && $row[0] !== '$') { + $row += $insertRows; + $formula = substr($formula, 0, $rowOffset) . $row . substr($formula, $rowOffset + $rowLength); + } + } + + return $formula; + } + + private function updateColumnRangesAllWorksheets(string $formula, int $insertColumns): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '/mui', + $formula, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $fromColumnLengths = array_map('strlen', array_column($splitRanges[1], 0)); + $fromColumnOffsets = array_column($splitRanges[1], 1); + $toColumnLengths = array_map('strlen', array_column($splitRanges[2], 0)); + $toColumnOffsets = array_column($splitRanges[2], 1); + + $fromColumns = $splitRanges[1]; + $toColumns = $splitRanges[2]; + + while ($splitCount > 0) { + --$splitCount; + $fromColumnLength = $fromColumnLengths[$splitCount]; + $toColumnLength = $toColumnLengths[$splitCount]; + $fromColumnOffset = $fromColumnOffsets[$splitCount]; + $toColumnOffset = $toColumnOffsets[$splitCount]; + $fromColumn = $fromColumns[$splitCount][0]; + $toColumn = $toColumns[$splitCount][0]; + + if (!empty($fromColumn) && $fromColumn[0] !== '$') { + $fromColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($fromColumn) + $insertColumns); + $formula = substr($formula, 0, $fromColumnOffset) . $fromColumn . substr($formula, $fromColumnOffset + $fromColumnLength); + } + if (!empty($toColumn) && $toColumn[0] !== '$') { + $toColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($toColumn) + $insertColumns); + $formula = substr($formula, 0, $toColumnOffset) . $toColumn . substr($formula, $toColumnOffset + $toColumnLength); + } + } + + return $formula; + } + + private function updateRowRangesAllWorksheets(string $formula, int $insertRows): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '/mui', + $formula, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $fromRowLengths = array_map('strlen', array_column($splitRanges[1], 0)); + $fromRowOffsets = array_column($splitRanges[1], 1); + $toRowLengths = array_map('strlen', array_column($splitRanges[2], 0)); + $toRowOffsets = array_column($splitRanges[2], 1); + + $fromRows = $splitRanges[1]; + $toRows = $splitRanges[2]; + + while ($splitCount > 0) { + --$splitCount; + $fromRowLength = $fromRowLengths[$splitCount]; + $toRowLength = $toRowLengths[$splitCount]; + $fromRowOffset = $fromRowOffsets[$splitCount]; + $toRowOffset = $toRowOffsets[$splitCount]; + $fromRow = $fromRows[$splitCount][0]; + $toRow = $toRows[$splitCount][0]; + + if (!empty($fromRow) && $fromRow[0] !== '$') { + $fromRow += $insertRows; + $formula = substr($formula, 0, $fromRowOffset) . $fromRow . substr($formula, $fromRowOffset + $fromRowLength); + } + if (!empty($toRow) && $toRow[0] !== '$') { + $toRow += $insertRows; + $formula = substr($formula, 0, $toRowOffset) . $toRow . substr($formula, $toRowOffset + $toRowLength); + } + } + + return $formula; + } + /** * Update cell reference. * @@ -766,8 +902,6 @@ class ReferenceHelper * @param int $pNumCols Number of columns to increment * @param int $pNumRows Number of rows to increment * - * @throws Exception - * * @return string Updated cell range */ public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) @@ -795,7 +929,7 @@ class ReferenceHelper * @param string $oldName Old name (name to replace) * @param string $newName New name */ - public function updateNamedFormulas(Spreadsheet $spreadsheet, $oldName = '', $newName = '') + public function updateNamedFormulas(Spreadsheet $spreadsheet, $oldName = '', $newName = ''): void { if ($oldName == '') { return; @@ -824,8 +958,6 @@ class ReferenceHelper * @param int $pNumCols Number of columns to increment * @param int $pNumRows Number of rows to increment * - * @throws Exception - * * @return string Updated cell range */ private function updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) @@ -864,8 +996,6 @@ class ReferenceHelper * @param int $pNumCols Number of columns to increment * @param int $pNumRows Number of rows to increment * - * @throws Exception - * * @return string Updated cell reference */ private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) @@ -900,8 +1030,6 @@ class ReferenceHelper /** * __clone implementation. Cloning should not be allowed in a Singleton! - * - * @throws Exception */ final public function __clone() { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php index 76a04d549f5..104177bd299 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php @@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\RichText; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\DataType; -use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\IComparable; class RichText implements IComparable @@ -20,10 +19,8 @@ class RichText implements IComparable * Create a new RichText instance. * * @param Cell $pCell - * - * @throws Exception */ - public function __construct(Cell $pCell = null) + public function __construct(?Cell $pCell = null) { // Initialise variables $this->richTextElements = []; @@ -47,7 +44,7 @@ class RichText implements IComparable * * @param ITextElement $pText Rich text element * - * @return RichText + * @return $this */ public function addText(ITextElement $pText) { @@ -61,8 +58,6 @@ class RichText implements IComparable * * @param string $pText Text * - * @throws Exception - * * @return TextElement */ public function createText($pText) @@ -78,8 +73,6 @@ class RichText implements IComparable * * @param string $pText Text * - * @throws Exception - * * @return Run */ public function createTextRun($pText) @@ -133,7 +126,7 @@ class RichText implements IComparable * * @param ITextElement[] $textElements Array of elements * - * @return RichText + * @return $this */ public function setRichTextElements(array $textElements) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php index b4996235f33..592d0e36b68 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php @@ -40,9 +40,9 @@ class Run extends TextElement implements ITextElement * * @param Font $pFont Font * - * @return ITextElement + * @return $this */ - public function setFont(Font $pFont = null) + public function setFont(?Font $pFont = null) { $this->font = $pFont; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php index d9ad0d7f5ed..f8be5d55b0e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php @@ -37,7 +37,7 @@ class TextElement implements ITextElement * * @param $text string Text * - * @return ITextElement + * @return $this */ public function setText($text) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php index 03fa6ac2cbe..cfa505733c6 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php @@ -5,6 +5,8 @@ namespace PhpOffice\PhpSpreadsheet; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Chart\Renderer\IRenderer; use PhpOffice\PhpSpreadsheet\Collection\Memory; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; use Psr\SimpleCache\CacheInterface; class Settings @@ -28,8 +30,6 @@ class Settings * Allow/disallow libxml_disable_entity_loader() call when not thread safe. * Default behaviour is to do the check, but if you're running PHP versions * 7.2 < 7.2.1 - * 7.1 < 7.1.13 - * 7.0 < 7.0.27 * then you may need to disable this check to prevent unwanted behaviour in other threads * SECURITY WARNING: Changing this flag is not recommended. * @@ -44,6 +44,18 @@ class Settings */ private static $cache; + /** + * The HTTP client implementation to be used for network request. + * + * @var null|ClientInterface + */ + private static $httpClient; + + /** + * @var null|RequestFactoryInterface + */ + private static $requestFactory; + /** * Set the locale code to use for formula translations and any special formatting. * @@ -61,10 +73,8 @@ class Settings * * @param string $rendererClass Class name of the chart renderer * eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph - * - * @throws Exception */ - public static function setChartRenderer($rendererClass) + public static function setChartRenderer($rendererClass): void { if (!is_a($rendererClass, IRenderer::class, true)) { throw new Exception('Chart renderer must implement ' . IRenderer::class); @@ -89,7 +99,7 @@ class Settings * * @param int $options Default options for libxml loader */ - public static function setLibXmlLoaderOptions($options) + public static function setLibXmlLoaderOptions($options): void { if ($options === null && defined('LIBXML_DTDLOAD')) { $options = LIBXML_DTDLOAD | LIBXML_DTDATTR; @@ -119,14 +129,12 @@ class Settings * Allow/disallow libxml_disable_entity_loader() call when not thread safe. * Default behaviour is to do the check, but if you're running PHP versions * 7.2 < 7.2.1 - * 7.1 < 7.1.13 - * 7.0 < 7.0.27 * then you may need to disable this check to prevent unwanted behaviour in other threads * SECURITY WARNING: Changing this flag to false is not recommended. * * @param bool $state */ - public static function setLibXmlDisableEntityLoader($state) + public static function setLibXmlDisableEntityLoader($state): void { self::$libXmlDisableEntityLoader = (bool) $state; } @@ -143,10 +151,8 @@ class Settings /** * Sets the implementation of cache that should be used for cell collection. - * - * @param CacheInterface $cache */ - public static function setCache(CacheInterface $cache) + public static function setCache(CacheInterface $cache): void { self::$cache = $cache; } @@ -164,4 +170,49 @@ class Settings return self::$cache; } + + /** + * Set the HTTP client implementation to be used for network request. + */ + public static function setHttpClient(ClientInterface $httpClient, RequestFactoryInterface $requestFactory): void + { + self::$httpClient = $httpClient; + self::$requestFactory = $requestFactory; + } + + /** + * Unset the HTTP client configuration. + */ + public static function unsetHttpClient(): void + { + self::$httpClient = null; + self::$requestFactory = null; + } + + /** + * Get the HTTP client implementation to be used for network request. + */ + public static function getHttpClient(): ClientInterface + { + self::assertHttpClient(); + + return self::$httpClient; + } + + /** + * Get the HTTP request factory. + */ + public static function getRequestFactory(): RequestFactoryInterface + { + self::assertHttpClient(); + + return self::$requestFactory; + } + + private static function assertHttpClient(): void + { + if (!self::$httpClient || !self::$requestFactory) { + throw new Exception('HTTP client must be configured via Settings::setHttpClient() to be able to use WEBSERVICE function.'); + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php index 4b57824203e..1d5d8933ed0 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php @@ -6,133 +6,94 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; class CodePage { + public const DEFAULT_CODE_PAGE = 'CP1252'; + + private static $pageArray = [ + 0 => 'CP1252', // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program + 367 => 'ASCII', // ASCII + 437 => 'CP437', // OEM US + //720 => 'notsupported', // OEM Arabic + 737 => 'CP737', // OEM Greek + 775 => 'CP775', // OEM Baltic + 850 => 'CP850', // OEM Latin I + 852 => 'CP852', // OEM Latin II (Central European) + 855 => 'CP855', // OEM Cyrillic + 857 => 'CP857', // OEM Turkish + 858 => 'CP858', // OEM Multilingual Latin I with Euro + 860 => 'CP860', // OEM Portugese + 861 => 'CP861', // OEM Icelandic + 862 => 'CP862', // OEM Hebrew + 863 => 'CP863', // OEM Canadian (French) + 864 => 'CP864', // OEM Arabic + 865 => 'CP865', // OEM Nordic + 866 => 'CP866', // OEM Cyrillic (Russian) + 869 => 'CP869', // OEM Greek (Modern) + 874 => 'CP874', // ANSI Thai + 932 => 'CP932', // ANSI Japanese Shift-JIS + 936 => 'CP936', // ANSI Chinese Simplified GBK + 949 => 'CP949', // ANSI Korean (Wansung) + 950 => 'CP950', // ANSI Chinese Traditional BIG5 + 1200 => 'UTF-16LE', // UTF-16 (BIFF8) + 1250 => 'CP1250', // ANSI Latin II (Central European) + 1251 => 'CP1251', // ANSI Cyrillic + 1252 => 'CP1252', // ANSI Latin I (BIFF4-BIFF7) + 1253 => 'CP1253', // ANSI Greek + 1254 => 'CP1254', // ANSI Turkish + 1255 => 'CP1255', // ANSI Hebrew + 1256 => 'CP1256', // ANSI Arabic + 1257 => 'CP1257', // ANSI Baltic + 1258 => 'CP1258', // ANSI Vietnamese + 1361 => 'CP1361', // ANSI Korean (Johab) + 10000 => 'MAC', // Apple Roman + 10001 => 'CP932', // Macintosh Japanese + 10002 => 'CP950', // Macintosh Chinese Traditional + 10003 => 'CP1361', // Macintosh Korean + 10004 => 'MACARABIC', // Apple Arabic + 10005 => 'MACHEBREW', // Apple Hebrew + 10006 => 'MACGREEK', // Macintosh Greek + 10007 => 'MACCYRILLIC', // Macintosh Cyrillic + 10008 => 'CP936', // Macintosh - Simplified Chinese (GB 2312) + 10010 => 'MACROMANIA', // Macintosh Romania + 10017 => 'MACUKRAINE', // Macintosh Ukraine + 10021 => 'MACTHAI', // Macintosh Thai + 10029 => 'MACCENTRALEUROPE', // Macintosh Central Europe + 10079 => 'MACICELAND', // Macintosh Icelandic + 10081 => 'MACTURKISH', // Macintosh Turkish + 10082 => 'MACCROATIAN', // Macintosh Croatian + 21010 => 'UTF-16LE', // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE + 32768 => 'MAC', // Apple Roman + //32769 => 'unsupported', // ANSI Latin I (BIFF2-BIFF3) + 65000 => 'UTF-7', // Unicode (UTF-7) + 65001 => 'UTF-8', // Unicode (UTF-8) + ]; + + public static function validate(string $codePage): bool + { + return in_array($codePage, self::$pageArray, true); + } + /** * Convert Microsoft Code Page Identifier to Code Page Name which iconv * and mbstring understands. * * @param int $codePage Microsoft Code Page Indentifier * - * @throws PhpSpreadsheetException - * * @return string Code Page Name */ - public static function numberToName($codePage) + public static function numberToName(int $codePage): string { - switch ($codePage) { - case 367: - return 'ASCII'; // ASCII - case 437: - return 'CP437'; // OEM US - case 720: - throw new PhpSpreadsheetException('Code page 720 not supported.'); // OEM Arabic - case 737: - return 'CP737'; // OEM Greek - case 775: - return 'CP775'; // OEM Baltic - case 850: - return 'CP850'; // OEM Latin I - case 852: - return 'CP852'; // OEM Latin II (Central European) - case 855: - return 'CP855'; // OEM Cyrillic - case 857: - return 'CP857'; // OEM Turkish - case 858: - return 'CP858'; // OEM Multilingual Latin I with Euro - case 860: - return 'CP860'; // OEM Portugese - case 861: - return 'CP861'; // OEM Icelandic - case 862: - return 'CP862'; // OEM Hebrew - case 863: - return 'CP863'; // OEM Canadian (French) - case 864: - return 'CP864'; // OEM Arabic - case 865: - return 'CP865'; // OEM Nordic - case 866: - return 'CP866'; // OEM Cyrillic (Russian) - case 869: - return 'CP869'; // OEM Greek (Modern) - case 874: - return 'CP874'; // ANSI Thai - case 932: - return 'CP932'; // ANSI Japanese Shift-JIS - case 936: - return 'CP936'; // ANSI Chinese Simplified GBK - case 949: - return 'CP949'; // ANSI Korean (Wansung) - case 950: - return 'CP950'; // ANSI Chinese Traditional BIG5 - case 1200: - return 'UTF-16LE'; // UTF-16 (BIFF8) - case 1250: - return 'CP1250'; // ANSI Latin II (Central European) - case 1251: - return 'CP1251'; // ANSI Cyrillic - case 0: - // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program - case 1252: - return 'CP1252'; // ANSI Latin I (BIFF4-BIFF7) - case 1253: - return 'CP1253'; // ANSI Greek - case 1254: - return 'CP1254'; // ANSI Turkish - case 1255: - return 'CP1255'; // ANSI Hebrew - case 1256: - return 'CP1256'; // ANSI Arabic - case 1257: - return 'CP1257'; // ANSI Baltic - case 1258: - return 'CP1258'; // ANSI Vietnamese - case 1361: - return 'CP1361'; // ANSI Korean (Johab) - case 10000: - return 'MAC'; // Apple Roman - case 10001: - return 'CP932'; // Macintosh Japanese - case 10002: - return 'CP950'; // Macintosh Chinese Traditional - case 10003: - return 'CP1361'; // Macintosh Korean - case 10004: - return 'MACARABIC'; // Apple Arabic - case 10005: - return 'MACHEBREW'; // Apple Hebrew - case 10006: - return 'MACGREEK'; // Macintosh Greek - case 10007: - return 'MACCYRILLIC'; // Macintosh Cyrillic - case 10008: - return 'CP936'; // Macintosh - Simplified Chinese (GB 2312) - case 10010: - return 'MACROMANIA'; // Macintosh Romania - case 10017: - return 'MACUKRAINE'; // Macintosh Ukraine - case 10021: - return 'MACTHAI'; // Macintosh Thai - case 10029: - return 'MACCENTRALEUROPE'; // Macintosh Central Europe - case 10079: - return 'MACICELAND'; // Macintosh Icelandic - case 10081: - return 'MACTURKISH'; // Macintosh Turkish - case 10082: - return 'MACCROATIAN'; // Macintosh Croatian - case 21010: - return 'UTF-16LE'; // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE - case 32768: - return 'MAC'; // Apple Roman - case 32769: - throw new PhpSpreadsheetException('Code page 32769 not supported.'); // ANSI Latin I (BIFF2-BIFF3) - case 65000: - return 'UTF-7'; // Unicode (UTF-7) - case 65001: - return 'UTF-8'; // Unicode (UTF-8) + if (array_key_exists($codePage, self::$pageArray)) { + return self::$pageArray[$codePage]; + } + if ($codePage == 720 || $codePage == 32769) { + throw new PhpSpreadsheetException("Code page $codePage not supported."); // OEM Arabic } throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage); } + + public static function getEncodings(): array + { + return self::$pageArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php index 5d2deb32c95..180a71596d6 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php @@ -7,6 +7,7 @@ use DateTimeZone; use PhpOffice\PhpSpreadsheet\Calculation\DateTime; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Cell\Cell; +use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; class Date @@ -57,7 +58,7 @@ class Date /** * Default timezone to use for DateTime objects. * - * @var null|\DateTimeZone + * @var null|DateTimeZone */ protected static $defaultTimeZone; @@ -70,8 +71,10 @@ class Date */ public static function setExcelCalendar($baseDate) { - if (($baseDate == self::CALENDAR_WINDOWS_1900) || - ($baseDate == self::CALENDAR_MAC_1904)) { + if ( + ($baseDate == self::CALENDAR_WINDOWS_1900) || + ($baseDate == self::CALENDAR_MAC_1904) + ) { self::$excelCalendar = $baseDate; return true; @@ -95,20 +98,19 @@ class Date * * @param DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions * - * @throws \Exception - * - * @return bool Success or failure * @return bool Success or failure */ public static function setDefaultTimezone($timeZone) { - if ($timeZone = self::validateTimeZone($timeZone)) { + try { + $timeZone = self::validateTimeZone($timeZone); self::$defaultTimeZone = $timeZone; - - return true; + $retval = true; + } catch (PhpSpreadsheetException $e) { + $retval = false; } - return false; + return $retval; } /** @@ -130,20 +132,18 @@ class Date * * @param DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object * - * @throws \Exception - * - * @return DateTimeZone The timezone as a timezone object * @return DateTimeZone The timezone as a timezone object */ - protected static function validateTimeZone($timeZone) + private static function validateTimeZone($timeZone) { - if (is_object($timeZone) && $timeZone instanceof DateTimeZone) { + if ($timeZone instanceof DateTimeZone) { return $timeZone; - } elseif (is_string($timeZone)) { + } + if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) { return new DateTimeZone($timeZone); } - throw new \Exception('Invalid timezone'); + throw new PhpSpreadsheetException('Invalid timezone'); } /** @@ -154,8 +154,6 @@ class Date * if you don't want to treat it as a UTC value * Use the default (UST) unless you absolutely need a conversion * - * @throws \Exception - * * @return \DateTime PHP date/time object */ public static function excelToDateTimeObject($excelTimestamp, $timeZone = null) @@ -203,8 +201,6 @@ class Date * if you don't want to treat it as a UTC value * Use the default (UST) unless you absolutely need a conversion * - * @throws \Exception - * * @return int Unix timetamp for this date/time */ public static function excelToTimestamp($excelTimestamp, $timeZone = null) @@ -319,13 +315,11 @@ class Date /** * Is a given cell a date/time? * - * @param Cell $pCell - * * @return bool */ public static function isDateTime(Cell $pCell) { - return is_numeric($pCell->getValue()) && + return is_numeric($pCell->getCalculatedValue()) && self::isDateTimeFormat( $pCell->getWorksheet()->getStyle( $pCell->getCoordinate() @@ -336,8 +330,6 @@ class Date /** * Is a given number format a date/time? * - * @param NumberFormat $pFormat - * * @return bool */ public static function isDateTimeFormat(NumberFormat $pFormat) @@ -397,6 +389,11 @@ class Date if ((substr($pFormatCode, 0, 1) == '_') || (substr($pFormatCode, 0, 2) == '0 ')) { return false; } + // Some "special formats" provided in German Excel versions were detected as date time value, + // so filter them out here - "\C\H\-00000" (Switzerland) and "\D-00000" (Germany). + if (\strpos($pFormatCode, '-00000') !== false) { + return false; + } // Try checking for any of the date formatting characters that don't appear within square braces if (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $pFormatCode)) { // We might also have a format mask containing quoted strings... @@ -405,8 +402,10 @@ class Date $segMatcher = false; foreach (explode('"', $pFormatCode) as $subVal) { // Only test in alternate array entries (the non-quoted blocks) - if (($segMatcher = !$segMatcher) && - (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $subVal))) { + if ( + ($segMatcher = !$segMatcher) && + (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $subVal)) + ) { return true; } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php index 25d6910d34e..f41fb695c46 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Shared; +use GdImage; + class Drawing { /** @@ -98,7 +100,7 @@ class Drawing */ public static function pixelsToPoints($pValue) { - return $pValue * 0.67777777; + return $pValue * 0.75; } /** @@ -111,7 +113,7 @@ class Drawing public static function pointsToPixels($pValue) { if ($pValue != 0) { - return (int) ceil($pValue * 1.333333333); + return (int) ceil($pValue / 0.75); } return 0; @@ -152,7 +154,7 @@ class Drawing * * @param string $p_sFile Path to Windows DIB (BMP) image * - * @return resource + * @return GdImage|resource */ public static function imagecreatefrombmp($p_sFile) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php index e9d387daa4b..b0d75d78330 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php @@ -25,7 +25,7 @@ class DgContainer return $this->dgId; } - public function setDgId($value) + public function setDgId($value): void { $this->dgId = $value; } @@ -35,7 +35,7 @@ class DgContainer return $this->lastSpId; } - public function setLastSpId($value) + public function setLastSpId($value): void { $this->lastSpId = $value; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php index 7e2c34608a2..1da877262fd 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php @@ -23,7 +23,7 @@ class SpgrContainer * * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer $parent */ - public function setParent($parent) + public function setParent($parent): void { $this->parent = $parent; } @@ -43,7 +43,7 @@ class SpgrContainer * * @param mixed $child */ - public function addChild($child) + public function addChild($child): void { $this->children[] = $child; $child->setParent($this); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php index bbf51df186e..8a81ff57974 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php @@ -95,7 +95,7 @@ class SpContainer * * @param SpgrContainer $parent */ - public function setParent($parent) + public function setParent($parent): void { $this->parent = $parent; } @@ -115,7 +115,7 @@ class SpContainer * * @param bool $value */ - public function setSpgr($value) + public function setSpgr($value): void { $this->spgr = $value; } @@ -135,7 +135,7 @@ class SpContainer * * @param int $value */ - public function setSpType($value) + public function setSpType($value): void { $this->spType = $value; } @@ -155,7 +155,7 @@ class SpContainer * * @param int $value */ - public function setSpFlag($value) + public function setSpFlag($value): void { $this->spFlag = $value; } @@ -175,7 +175,7 @@ class SpContainer * * @param int $value */ - public function setSpId($value) + public function setSpId($value): void { $this->spId = $value; } @@ -196,7 +196,7 @@ class SpContainer * @param int $property The number specifies the option * @param mixed $value */ - public function setOPT($property, $value) + public function setOPT($property, $value): void { $this->OPT[$property] = $value; } @@ -232,7 +232,7 @@ class SpContainer * * @param string $value eg: 'A1' */ - public function setStartCoordinates($value) + public function setStartCoordinates($value): void { $this->startCoordinates = $value; } @@ -252,7 +252,7 @@ class SpContainer * * @param int $startOffsetX */ - public function setStartOffsetX($startOffsetX) + public function setStartOffsetX($startOffsetX): void { $this->startOffsetX = $startOffsetX; } @@ -272,7 +272,7 @@ class SpContainer * * @param int $startOffsetY */ - public function setStartOffsetY($startOffsetY) + public function setStartOffsetY($startOffsetY): void { $this->startOffsetY = $startOffsetY; } @@ -292,7 +292,7 @@ class SpContainer * * @param string $value eg: 'A1' */ - public function setEndCoordinates($value) + public function setEndCoordinates($value): void { $this->endCoordinates = $value; } @@ -312,7 +312,7 @@ class SpContainer * * @param int $endOffsetX */ - public function setEndOffsetX($endOffsetX) + public function setEndOffsetX($endOffsetX): void { $this->endOffsetX = $endOffsetX; } @@ -332,7 +332,7 @@ class SpContainer * * @param int $endOffsetY */ - public function setEndOffsetY($endOffsetY) + public function setEndOffsetY($endOffsetY): void { $this->endOffsetY = $endOffsetY; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php index 96da32131e3..1bd15b9eddd 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php @@ -61,7 +61,7 @@ class DggContainer * * @param int $value */ - public function setSpIdMax($value) + public function setSpIdMax($value): void { $this->spIdMax = $value; } @@ -81,7 +81,7 @@ class DggContainer * * @param int $value */ - public function setCDgSaved($value) + public function setCDgSaved($value): void { $this->cDgSaved = $value; } @@ -101,7 +101,7 @@ class DggContainer * * @param int $value */ - public function setCSpSaved($value) + public function setCSpSaved($value): void { $this->cSpSaved = $value; } @@ -121,7 +121,7 @@ class DggContainer * * @param DggContainer\BstoreContainer $bstoreContainer */ - public function setBstoreContainer($bstoreContainer) + public function setBstoreContainer($bstoreContainer): void { $this->bstoreContainer = $bstoreContainer; } @@ -132,7 +132,7 @@ class DggContainer * @param int $property The number specifies the option * @param mixed $value */ - public function setOPT($property, $value) + public function setOPT($property, $value): void { $this->OPT[$property] = $value; } @@ -168,7 +168,7 @@ class DggContainer * * @param array $pValue */ - public function setIDCLs($pValue) + public function setIDCLs($pValue): void { $this->IDCLs = $pValue; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php index 9d1e68ec771..b07786ff658 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php @@ -16,7 +16,7 @@ class BstoreContainer * * @param BstoreContainer\BSE $BSE */ - public function addBSE($BSE) + public function addBSE($BSE): void { $this->BSECollection[] = $BSE; $BSE->setParent($this); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php index f83bdc7ee3f..e8851465771 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php @@ -41,7 +41,7 @@ class BSE * * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer $parent */ - public function setParent($parent) + public function setParent($parent): void { $this->parent = $parent; } @@ -61,7 +61,7 @@ class BSE * * @param BSE\Blip $blip */ - public function setBlip($blip) + public function setBlip($blip): void { $this->blip = $blip; $blip->setParent($this); @@ -82,7 +82,7 @@ class BSE * * @param int $blipType */ - public function setBlipType($blipType) + public function setBlipType($blipType): void { $this->blipType = $blipType; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php index 88bc117a166..500d7eaf3ee 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php @@ -33,7 +33,7 @@ class Blip * * @param string $data */ - public function setData($data) + public function setData($data): void { $this->data = $data; } @@ -43,7 +43,7 @@ class Blip * * @param \PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE $parent */ - public function setParent($parent) + public function setParent($parent): void { $this->parent = $parent; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php index 3532908bd55..7525df8a6b9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php @@ -19,7 +19,7 @@ class File * * @param bool $useUploadTempDir Use File Upload Temporary directory (true or false) */ - public static function setUseUploadTempDirectory($useUploadTempDir) + public static function setUseUploadTempDirectory($useUploadTempDir): void { self::$useUploadTempDirectory = (bool) $useUploadTempDir; } @@ -109,12 +109,6 @@ class File */ public static function sysGetTempDir() { - // Moodle hack! - if (function_exists('make_temp_directory')) { - $temp = make_temp_directory('phpspreadsheet'); - return realpath(dirname($temp)); - } - if (self::$useUploadTempDirectory) { // use upload-directory when defined to allow running on environments having very restricted // open_basedir configs @@ -134,10 +128,8 @@ class File * Assert that given path is an existing file and is readable, otherwise throw exception. * * @param string $filename - * - * @throws InvalidArgumentException */ - public static function assertFile($filename) + public static function assertFile($filename): void { if (!is_file($filename)) { throw new InvalidArgumentException('File "' . $filename . '" does not exist.'); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php index bee13e2921e..ee1f8abac85 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php @@ -198,7 +198,7 @@ class Font * * @param string $pValue */ - public static function setTrueTypeFontPath($pValue) + public static function setTrueTypeFontPath($pValue): void { self::$trueTypeFontPath = $pValue; } @@ -223,7 +223,7 @@ class Font * * @return int Column width */ - public static function calculateColumnWidth(\PhpOffice\PhpSpreadsheet\Style\Font $font, $cellText = '', $rotation = 0, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont = null) + public static function calculateColumnWidth(\PhpOffice\PhpSpreadsheet\Style\Font $font, $cellText = '', $rotation = 0, ?\PhpOffice\PhpSpreadsheet\Style\Font $defaultFont = null) { // If it is rich text, use plain text if ($cellText instanceof RichText) { @@ -276,8 +276,6 @@ class Font * @param \PhpOffice\PhpSpreadsheet\Style\Font * @param int $rotation * - * @throws PhpSpreadsheetException - * * @return int */ public static function getTextWidthPixelsExact($text, \PhpOffice\PhpSpreadsheet\Style\Font $font, $rotation = 0) @@ -305,7 +303,6 @@ class Font * Get approximate width in pixels for a string of text in a certain font at a certain rotation angle. * * @param string $columnText - * @param \PhpOffice\PhpSpreadsheet\Style\Font $font * @param int $rotation * * @return int Text width in pixels (no padding added) @@ -321,21 +318,25 @@ class Font // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font. $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText)); $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size + break; case 'Arial': // value 8 was set because of experience in different exports at Arial 10 font. $columnWidth = (int) (8 * StringHelper::countCharacters($columnText)); $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size + break; case 'Verdana': // value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font. $columnWidth = (int) (8 * StringHelper::countCharacters($columnText)); $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size + break; default: // just assume Calibri $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText)); $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size + break; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php index ba59e0e5ade..4c67c3a908f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/EigenvalueDecomposition.php @@ -73,7 +73,7 @@ class EigenvalueDecomposition /** * Symmetric Householder reduction to tridiagonal form. */ - private function tred2() + private function tred2(): void { // This is derived from the Algol procedures tred2 by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for @@ -96,7 +96,7 @@ class EigenvalueDecomposition // Generate Householder vector. for ($k = 0; $k < $i; ++$k) { $this->d[$k] /= $scale; - $h += pow($this->d[$k], 2); + $h += $this->d[$k] ** 2; } $f = $this->d[$i_]; $g = sqrt($h); @@ -180,7 +180,7 @@ class EigenvalueDecomposition * Auto. Comp., Vol.ii-Linear Algebra, and the corresponding * Fortran subroutine in EISPACK. */ - private function tql2() + private function tql2(): void { for ($i = 1; $i < $this->n; ++$i) { $this->e[$i - 1] = $this->e[$i]; @@ -188,7 +188,7 @@ class EigenvalueDecomposition $this->e[$this->n - 1] = 0.0; $f = 0.0; $tst1 = 0.0; - $eps = pow(2.0, -52.0); + $eps = 2.0 ** (-52.0); for ($l = 0; $l < $this->n; ++$l) { // Find small subdiagonal element @@ -206,7 +206,7 @@ class EigenvalueDecomposition $iter = 0; do { // Could check iteration count here. - $iter += 1; + ++$iter; // Compute implicit shift $g = $this->d[$l]; $p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]); @@ -287,7 +287,7 @@ class EigenvalueDecomposition * Vol.ii-Linear Algebra, and the corresponding * Fortran subroutines in EISPACK. */ - private function orthes() + private function orthes(): void { $low = 0; $high = $this->n - 1; @@ -372,7 +372,7 @@ class EigenvalueDecomposition * @param mixed $yr * @param mixed $yi */ - private function cdiv($xr, $xi, $yr, $yi) + private function cdiv($xr, $xi, $yr, $yi): void { if (abs($yr) > abs($yi)) { $r = $yi / $yr; @@ -395,21 +395,21 @@ class EigenvalueDecomposition * Vol.ii-Linear Algebra, and the corresponding * Fortran subroutine in EISPACK. */ - private function hqr2() + private function hqr2(): void { // Initialize $nn = $this->n; $n = $nn - 1; $low = 0; $high = $nn - 1; - $eps = pow(2.0, -52.0); + $eps = 2.0 ** (-52.0); $exshift = 0.0; $p = $q = $r = $s = $z = 0; // Store roots isolated by balanc and compute matrix norm $norm = 0.0; for ($i = 0; $i < $nn; ++$i) { - if (($i < $low) or ($i > $high)) { + if (($i < $low) || ($i > $high)) { $this->d[$i] = $this->H[$i][$i]; $this->e[$i] = 0.0; } @@ -553,8 +553,10 @@ class EigenvalueDecomposition if ($m == $l) { break; } - if (abs($this->H[$m][$m - 1]) * (abs($q) + abs($r)) < - $eps * (abs($p) * (abs($this->H[$m - 1][$m - 1]) + abs($z) + abs($this->H[$m + 1][$m + 1])))) { + if ( + abs($this->H[$m][$m - 1]) * (abs($q) + abs($r)) < + $eps * (abs($p) * (abs($this->H[$m - 1][$m - 1]) + abs($z) + abs($this->H[$m + 1][$m + 1]))) + ) { break; } --$m; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php index bb2b4b040a9..4aecff73431 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php @@ -242,9 +242,6 @@ class LUDecomposition * * @param mixed $B a Matrix with as many rows as A and any number of columns * - * @throws CalculationException illegalArgumentException Matrix row dimensions must agree - * @throws CalculationException runtimeException Matrix is singular - * * @return Matrix X so that L*U*X = B(piv,:) */ public function solve($B) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php index 17f321077dd..a5cb6de0f61 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php @@ -159,11 +159,6 @@ class Matrix * * Get a submatrix * - * @param int $i0 Initial row index - * @param int $iF Final row index - * @param int $j0 Initial column index - * @param int $jF Final column index - * * @return Matrix Submatrix */ public function getMatrix(...$args) @@ -477,8 +472,6 @@ class Matrix * * A + B * - * @param mixed $B Matrix/Array - * * @return Matrix Sum */ public function plus(...$args) @@ -522,9 +515,7 @@ class Matrix * * A = A + B * - * @param mixed $B Matrix/Array - * - * @return Matrix Sum + * @return $this */ public function plusEquals(...$args) { @@ -581,8 +572,6 @@ class Matrix * * A - B * - * @param mixed $B Matrix/Array - * * @return Matrix Sum */ public function minus(...$args) @@ -626,9 +615,7 @@ class Matrix * * A = A - B * - * @param mixed $B Matrix/Array - * - * @return Matrix Sum + * @return $this */ public function minusEquals(...$args) { @@ -686,8 +673,6 @@ class Matrix * Element-by-element multiplication * Cij = Aij * Bij * - * @param mixed $B Matrix/Array - * * @return Matrix Matrix Cij */ public function arrayTimes(...$args) @@ -732,9 +717,7 @@ class Matrix * Element-by-element multiplication * Aij = Aij * Bij * - * @param mixed $B Matrix/Array - * - * @return Matrix Matrix Aij + * @return $this */ public function arrayTimesEquals(...$args) { @@ -792,8 +775,6 @@ class Matrix * Element-by-element right division * A / B * - * @param Matrix $B Matrix B - * * @return Matrix Division result */ public function arrayRightDivide(...$args) @@ -857,8 +838,6 @@ class Matrix * Element-by-element right division * Aij = Aij / Bij * - * @param mixed $B Matrix/Array - * * @return Matrix Matrix Aij */ public function arrayRightDivideEquals(...$args) @@ -903,8 +882,6 @@ class Matrix * Element-by-element Left division * A / B * - * @param Matrix $B Matrix B - * * @return Matrix Division result */ public function arrayLeftDivide(...$args) @@ -949,8 +926,6 @@ class Matrix * Element-by-element Left division * Aij = Aij / Bij * - * @param mixed $B Matrix/Array - * * @return Matrix Matrix Aij */ public function arrayLeftDivideEquals(...$args) @@ -994,8 +969,6 @@ class Matrix * * Matrix multiplication * - * @param mixed $n Matrix/Array/Scalar - * * @return Matrix Product */ public function times(...$args) @@ -1089,9 +1062,7 @@ class Matrix * * A = A ^ B * - * @param mixed $B Matrix/Array - * - * @return Matrix Sum + * @return $this */ public function power(...$args) { @@ -1130,7 +1101,7 @@ class Matrix $validValues &= StringHelper::convertToNumberIfFraction($value); } if ($validValues) { - $this->A[$i][$j] = pow($this->A[$i][$j], $value); + $this->A[$i][$j] = $this->A[$i][$j] ** $value; } else { $this->A[$i][$j] = Functions::NAN(); } @@ -1148,9 +1119,7 @@ class Matrix * * A = A & B * - * @param mixed $B Matrix/Array - * - * @return Matrix Sum + * @return $this */ public function concat(...$args) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php index e666d74b6a3..3bb8a10ef91 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php @@ -60,7 +60,7 @@ class QRDecomposition { if ($A instanceof Matrix) { // Initialize. - $this->QR = $A->getArrayCopy(); + $this->QR = $A->getArray(); $this->m = $A->getRowDimension(); $this->n = $A->getColumnDimension(); // Main loop. diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php index 3ca95619c86..b997fb7cc1c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php @@ -117,7 +117,7 @@ class SingularValueDecomposition } } - if ($wantu and ($k < $nct)) { + if ($wantu && ($k < $nct)) { // Place the transformation in U for subsequent back // multiplication. for ($i = $k; $i < $this->m; ++$i) { @@ -143,7 +143,7 @@ class SingularValueDecomposition $e[$k + 1] += 1.0; } $e[$k] = -$e[$k]; - if (($k + 1 < $this->m) and ($e[$k] != 0.0)) { + if (($k + 1 < $this->m) && ($e[$k] != 0.0)) { // Apply the transformation. for ($i = $k + 1; $i < $this->m; ++$i) { $work[$i] = 0.0; @@ -221,7 +221,7 @@ class SingularValueDecomposition // If required, generate V. if ($wantv) { for ($k = $this->n - 1; $k >= 0; --$k) { - if (($k < $nrt) and ($e[$k] != 0.0)) { + if (($k < $nrt) && ($e[$k] != 0.0)) { for ($j = $k + 1; $j < $nu; ++$j) { $t = 0; for ($i = $k + 1; $i < $this->n; ++$i) { @@ -243,7 +243,7 @@ class SingularValueDecomposition // Main iteration loop for the singular values. $pp = $p - 1; $iter = 0; - $eps = pow(2.0, -52.0); + $eps = 2.0 ** (-52.0); while ($p > 0) { // Here is where a test for too many iterations would go. @@ -415,14 +415,14 @@ class SingularValueDecomposition $t = $this->s[$k]; $this->s[$k] = $this->s[$k + 1]; $this->s[$k + 1] = $t; - if ($wantv and ($k < $this->n - 1)) { + if ($wantv && ($k < $this->n - 1)) { for ($i = 0; $i < $this->n; ++$i) { $t = $this->V[$i][$k + 1]; $this->V[$i][$k + 1] = $this->V[$i][$k]; $this->V[$i][$k] = $t; } } - if ($wantu and ($k < $this->m - 1)) { + if ($wantu && ($k < $this->m - 1)) { for ($i = 0; $i < $this->m; ++$i) { $t = $this->U[$i][$k + 1]; $this->U[$i][$k + 1] = $this->U[$i][$k]; @@ -513,7 +513,7 @@ class SingularValueDecomposition */ public function rank() { - $eps = pow(2.0, -52.0); + $eps = 2.0 ** (-52.0); $tol = max($this->m, $this->n) * $this->s[0] * $eps; $r = 0; $iMax = count($this->s); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php index 68c3864932c..49877b23eae 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php @@ -1,4 +1,5 @@ | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// + +use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; +use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream; +use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root; + +/* + * Array for storing OLE instances that are accessed from + * OLE_ChainedBlockStream::stream_open(). + * + * @var array + */ +$GLOBALS['_OLE_INSTANCES'] = []; + +/** + * OLE package base class. + * + * @author Xavier Noguer + * @author Christian Schmidt + */ +class OLE +{ + const OLE_PPS_TYPE_ROOT = 5; + const OLE_PPS_TYPE_DIR = 1; + const OLE_PPS_TYPE_FILE = 2; + const OLE_DATA_SIZE_SMALL = 0x1000; + const OLE_LONG_INT_SIZE = 4; + const OLE_PPS_SIZE = 0x80; + + /** + * The file handle for reading an OLE container. + * + * @var resource + */ + public $_file_handle; + + /** + * Array of PPS's found on the OLE container. + * + * @var array + */ + public $_list = []; + + /** + * Root directory of OLE container. + * + * @var Root + */ + public $root; + + /** + * Big Block Allocation Table. + * + * @var array (blockId => nextBlockId) + */ + public $bbat; + + /** + * Short Block Allocation Table. + * + * @var array (blockId => nextBlockId) + */ + public $sbat; + + /** + * Size of big blocks. This is usually 512. + * + * @var int number of octets per block + */ + public $bigBlockSize; + + /** + * Size of small blocks. This is usually 64. + * + * @var int number of octets per block + */ + public $smallBlockSize; + + /** + * Threshold for big blocks. + * + * @var int + */ + public $bigBlockThreshold; + + /** + * Reads an OLE container from the contents of the file given. + * + * @acces public + * + * @param string $file + * + * @return bool true on success, PEAR_Error on failure + */ + public function read($file) + { + $fh = fopen($file, 'rb'); + if (!$fh) { + throw new ReaderException("Can't open file $file"); + } + $this->_file_handle = $fh; + + $signature = fread($fh, 8); + if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) { + throw new ReaderException("File doesn't seem to be an OLE container."); + } + fseek($fh, 28); + if (fread($fh, 2) != "\xFE\xFF") { + // This shouldn't be a problem in practice + throw new ReaderException('Only Little-Endian encoding is supported.'); + } + // Size of blocks and short blocks in bytes + $this->bigBlockSize = 2 ** self::readInt2($fh); + $this->smallBlockSize = 2 ** self::readInt2($fh); + + // Skip UID, revision number and version number + fseek($fh, 44); + // Number of blocks in Big Block Allocation Table + $bbatBlockCount = self::readInt4($fh); + + // Root chain 1st block + $directoryFirstBlockId = self::readInt4($fh); + + // Skip unused bytes + fseek($fh, 56); + // Streams shorter than this are stored using small blocks + $this->bigBlockThreshold = self::readInt4($fh); + // Block id of first sector in Short Block Allocation Table + $sbatFirstBlockId = self::readInt4($fh); + // Number of blocks in Short Block Allocation Table + $sbbatBlockCount = self::readInt4($fh); + // Block id of first sector in Master Block Allocation Table + $mbatFirstBlockId = self::readInt4($fh); + // Number of blocks in Master Block Allocation Table + $mbbatBlockCount = self::readInt4($fh); + $this->bbat = []; + + // Remaining 4 * 109 bytes of current block is beginning of Master + // Block Allocation Table + $mbatBlocks = []; + for ($i = 0; $i < 109; ++$i) { + $mbatBlocks[] = self::readInt4($fh); + } + + // Read rest of Master Block Allocation Table (if any is left) + $pos = $this->getBlockOffset($mbatFirstBlockId); + for ($i = 0; $i < $mbbatBlockCount; ++$i) { + fseek($fh, $pos); + for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) { + $mbatBlocks[] = self::readInt4($fh); + } + // Last block id in each block points to next block + $pos = $this->getBlockOffset(self::readInt4($fh)); + } + + // Read Big Block Allocation Table according to chain specified by $mbatBlocks + for ($i = 0; $i < $bbatBlockCount; ++$i) { + $pos = $this->getBlockOffset($mbatBlocks[$i]); + fseek($fh, $pos); + for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) { + $this->bbat[] = self::readInt4($fh); + } + } + + // Read short block allocation table (SBAT) + $this->sbat = []; + $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4; + $sbatFh = $this->getStream($sbatFirstBlockId); + for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) { + $this->sbat[$blockId] = self::readInt4($sbatFh); + } + fclose($sbatFh); + + $this->readPpsWks($directoryFirstBlockId); + + return true; + } + + /** + * @param int $blockId byte offset from beginning of file + * + * @return int + */ + public function getBlockOffset($blockId) + { + return 512 + $blockId * $this->bigBlockSize; + } + + /** + * Returns a stream for use with fread() etc. External callers should + * use \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::getStream(). + * + * @param int|OLE\PPS $blockIdOrPps block id or PPS + * + * @return resource read-only stream + */ + public function getStream($blockIdOrPps) + { + static $isRegistered = false; + if (!$isRegistered) { + stream_wrapper_register('ole-chainedblockstream', ChainedBlockStream::class); + $isRegistered = true; + } + + // Store current instance in global array, so that it can be accessed + // in OLE_ChainedBlockStream::stream_open(). + // Object is removed from self::$instances in OLE_Stream::close(). + $GLOBALS['_OLE_INSTANCES'][] = $this; + $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES'])); + + $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId; + if ($blockIdOrPps instanceof OLE\PPS) { + $path .= '&blockId=' . $blockIdOrPps->startBlock; + $path .= '&size=' . $blockIdOrPps->Size; + } else { + $path .= '&blockId=' . $blockIdOrPps; + } + + return fopen($path, 'rb'); + } + + /** + * Reads a signed char. + * + * @param resource $fh file handle + * + * @return int + */ + private static function readInt1($fh) + { + [, $tmp] = unpack('c', fread($fh, 1)); + + return $tmp; + } + + /** + * Reads an unsigned short (2 octets). + * + * @param resource $fh file handle + * + * @return int + */ + private static function readInt2($fh) + { + [, $tmp] = unpack('v', fread($fh, 2)); + + return $tmp; + } + + /** + * Reads an unsigned long (4 octets). + * + * @param resource $fh file handle + * + * @return int + */ + private static function readInt4($fh) + { + [, $tmp] = unpack('V', fread($fh, 4)); + + return $tmp; + } + + /** + * Gets information about all PPS's on the OLE container from the PPS WK's + * creates an OLE_PPS object for each one. + * + * @param int $blockId the block id of the first block + * + * @return bool true on success, PEAR_Error on failure + */ + public function readPpsWks($blockId) + { + $fh = $this->getStream($blockId); + for ($pos = 0; true; $pos += 128) { + fseek($fh, $pos, SEEK_SET); + $nameUtf16 = fread($fh, 64); + $nameLength = self::readInt2($fh); + $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2); + // Simple conversion from UTF-16LE to ISO-8859-1 + $name = str_replace("\x00", '', $nameUtf16); + $type = self::readInt1($fh); + switch ($type) { + case self::OLE_PPS_TYPE_ROOT: + $pps = new OLE\PPS\Root(null, null, []); + $this->root = $pps; + + break; + case self::OLE_PPS_TYPE_DIR: + $pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []); + + break; + case self::OLE_PPS_TYPE_FILE: + $pps = new OLE\PPS\File($name); + + break; + default: + break; + } + fseek($fh, 1, SEEK_CUR); + $pps->Type = $type; + $pps->Name = $name; + $pps->PrevPps = self::readInt4($fh); + $pps->NextPps = self::readInt4($fh); + $pps->DirPps = self::readInt4($fh); + fseek($fh, 20, SEEK_CUR); + $pps->Time1st = self::OLE2LocalDate(fread($fh, 8)); + $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8)); + $pps->startBlock = self::readInt4($fh); + $pps->Size = self::readInt4($fh); + $pps->No = count($this->_list); + $this->_list[] = $pps; + + // check if the PPS tree (starting from root) is complete + if (isset($this->root) && $this->ppsTreeComplete($this->root->No)) { + break; + } + } + fclose($fh); + + // Initialize $pps->children on directories + foreach ($this->_list as $pps) { + if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) { + $nos = [$pps->DirPps]; + $pps->children = []; + while ($nos) { + $no = array_pop($nos); + if ($no != -1) { + $childPps = $this->_list[$no]; + $nos[] = $childPps->PrevPps; + $nos[] = $childPps->NextPps; + $pps->children[] = $childPps; + } + } + } + } + + return true; + } + + /** + * It checks whether the PPS tree is complete (all PPS's read) + * starting with the given PPS (not necessarily root). + * + * @param int $index The index of the PPS from which we are checking + * + * @return bool Whether the PPS tree for the given PPS is complete + */ + private function ppsTreeComplete($index) + { + return isset($this->_list[$index]) && + ($pps = $this->_list[$index]) && + ($pps->PrevPps == -1 || + $this->ppsTreeComplete($pps->PrevPps)) && + ($pps->NextPps == -1 || + $this->ppsTreeComplete($pps->NextPps)) && + ($pps->DirPps == -1 || + $this->ppsTreeComplete($pps->DirPps)); + } + + /** + * Checks whether a PPS is a File PPS or not. + * If there is no PPS for the index given, it will return false. + * + * @param int $index The index for the PPS + * + * @return bool true if it's a File PPS, false otherwise + */ + public function isFile($index) + { + if (isset($this->_list[$index])) { + return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE; + } + + return false; + } + + /** + * Checks whether a PPS is a Root PPS or not. + * If there is no PPS for the index given, it will return false. + * + * @param int $index the index for the PPS + * + * @return bool true if it's a Root PPS, false otherwise + */ + public function isRoot($index) + { + if (isset($this->_list[$index])) { + return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT; + } + + return false; + } + + /** + * Gives the total number of PPS's found in the OLE container. + * + * @return int The total number of PPS's found in the OLE container + */ + public function ppsTotal() + { + return count($this->_list); + } + + /** + * Gets data from a PPS + * If there is no PPS for the index given, it will return an empty string. + * + * @param int $index The index for the PPS + * @param int $position The position from which to start reading + * (relative to the PPS) + * @param int $length The amount of bytes to read (at most) + * + * @return string The binary string containing the data requested + * + * @see OLE_PPS_File::getStream() + */ + public function getData($index, $position, $length) + { + // if position is not valid return empty string + if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) { + return ''; + } + $fh = $this->getStream($this->_list[$index]); + $data = stream_get_contents($fh, $length, $position); + fclose($fh); + + return $data; + } + + /** + * Gets the data length from a PPS + * If there is no PPS for the index given, it will return 0. + * + * @param int $index The index for the PPS + * + * @return int The amount of bytes in data the PPS has + */ + public function getDataLength($index) + { + if (isset($this->_list[$index])) { + return $this->_list[$index]->Size; + } + + return 0; + } + + /** + * Utility function to transform ASCII text to Unicode. + * + * @param string $ascii The ASCII string to transform + * + * @return string The string in Unicode + */ + public static function ascToUcs($ascii) + { + $rawname = ''; + $iMax = strlen($ascii); + for ($i = 0; $i < $iMax; ++$i) { + $rawname .= $ascii[$i] + . "\x00"; + } + + return $rawname; + } + + /** + * Utility function + * Returns a string for the OLE container with the date given. + * + * @param int $date A timestamp + * + * @return string The string for the OLE container + */ + public static function localDateToOLE($date) + { + if (!isset($date)) { + return "\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + // factor used for separating numbers into 4 bytes parts + $factor = 2 ** 32; + + // days from 1-1-1601 until the beggining of UNIX era + $days = 134774; + // calculate seconds + $big_date = $days * 24 * 3600 + mktime((int) date('H', $date), (int) date('i', $date), (int) date('s', $date), (int) date('m', $date), (int) date('d', $date), (int) date('Y', $date)); + // multiply just to make MS happy + $big_date *= 10000000; + + $high_part = floor($big_date / $factor); + // lower 4 bytes + $low_part = floor((($big_date / $factor) - $high_part) * $factor); + + // Make HEX string + $res = ''; + + for ($i = 0; $i < 4; ++$i) { + $hex = $low_part % 0x100; + $res .= pack('c', $hex); + $low_part /= 0x100; + } + for ($i = 0; $i < 4; ++$i) { + $hex = $high_part % 0x100; + $res .= pack('c', $hex); + $high_part /= 0x100; + } + + return $res; + } + + /** + * Returns a timestamp from an OLE container's date. + * + * @param string $oleTimestamp A binary string with the encoded date + * + * @return int The Unix timestamp corresponding to the string + */ + public static function OLE2LocalDate($oleTimestamp) + { + if (strlen($oleTimestamp) != 8) { + throw new ReaderException('Expecting 8 byte string'); + } + + // convert to units of 100 ns since 1601: + $unpackedTimestamp = unpack('v4', $oleTimestamp); + $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3]; + $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1]; + + // translate to seconds since 1601: + $timestampHigh /= 10000000; + $timestampLow /= 10000000; + + // days from 1601 to 1970: + $days = 134774; + + // translate to seconds since 1970: + $unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5); + + $iTimestamp = (int) $unixTimestamp; + + // Overflow conditions can't happen on 64-bit system + return ($iTimestamp == $unixTimestamp) ? $iTimestamp : ($unixTimestamp >= 0.0 ? PHP_INT_MAX : PHP_INT_MIN); + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php new file mode 100644 index 00000000000..cee5cd99d30 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php @@ -0,0 +1,196 @@ +params); + if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error('OLE stream not found', E_USER_WARNING); + } + + return false; + } + $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; + + $blockId = $this->params['blockId']; + $this->data = ''; + if (isset($this->params['size']) && $this->params['size'] < $this->ole->bigBlockThreshold && $blockId != $this->ole->root->startBlock) { + // Block id refers to small blocks + $rootPos = $this->ole->getBlockOffset($this->ole->root->startBlock); + while ($blockId != -2) { + $pos = $rootPos + $blockId * $this->ole->bigBlockSize; + $blockId = $this->ole->sbat[$blockId]; + fseek($this->ole->_file_handle, $pos); + $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); + } + } else { + // Block id refers to big blocks + while ($blockId != -2) { + $pos = $this->ole->getBlockOffset($blockId); + fseek($this->ole->_file_handle, $pos); + $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); + $blockId = $this->ole->bbat[$blockId]; + } + } + if (isset($this->params['size'])) { + $this->data = substr($this->data, 0, $this->params['size']); + } + + if ($options & STREAM_USE_PATH) { + $openedPath = $path; + } + + return true; + } + + /** + * Implements support for fclose(). + */ + public function stream_close(): void // @codingStandardsIgnoreLine + { + $this->ole = null; + unset($GLOBALS['_OLE_INSTANCES']); + } + + /** + * Implements support for fread(), fgets() etc. + * + * @param int $count maximum number of bytes to read + * + * @return string + */ + public function stream_read($count) // @codingStandardsIgnoreLine + { + if ($this->stream_eof()) { + return false; + } + $s = substr($this->data, $this->pos, $count); + $this->pos += $count; + + return $s; + } + + /** + * Implements support for feof(). + * + * @return bool TRUE if the file pointer is at EOF; otherwise FALSE + */ + public function stream_eof() // @codingStandardsIgnoreLine + { + return $this->pos >= strlen($this->data); + } + + /** + * Returns the position of the file pointer, i.e. its offset into the file + * stream. Implements support for ftell(). + * + * @return int + */ + public function stream_tell() // @codingStandardsIgnoreLine + { + return $this->pos; + } + + /** + * Implements support for fseek(). + * + * @param int $offset byte offset + * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END + * + * @return bool + */ + public function stream_seek($offset, $whence) // @codingStandardsIgnoreLine + { + if ($whence == SEEK_SET && $offset >= 0) { + $this->pos = $offset; + } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) { + $this->pos += $offset; + } elseif ($whence == SEEK_END && -$offset <= count($this->data)) { + $this->pos = strlen($this->data) + $offset; + } else { + return false; + } + + return true; + } + + /** + * Implements support for fstat(). Currently the only supported field is + * "size". + * + * @return array + */ + public function stream_stat() // @codingStandardsIgnoreLine + { + return [ + 'size' => strlen($this->data), + ]; + } + + // Methods used by stream_wrapper_register() that are not implemented: + // bool stream_flush ( void ) + // int stream_write ( string data ) + // bool rename ( string path_from, string path_to ) + // bool mkdir ( string path, int mode, int options ) + // bool rmdir ( string path, int options ) + // bool dir_opendir ( string path, int options ) + // array url_stat ( string path, int flags ) + // string dir_readdir ( void ) + // bool dir_rewinddir ( void ) + // bool dir_closedir ( void ) +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php new file mode 100644 index 00000000000..cf764d0bfa3 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php @@ -0,0 +1,237 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +use PhpOffice\PhpSpreadsheet\Shared\OLE; + +/** + * Class for creating PPS's for OLE containers. + * + * @author Xavier Noguer + */ +class PPS +{ + /** + * The PPS index. + * + * @var int + */ + public $No; + + /** + * The PPS name (in Unicode). + * + * @var string + */ + public $Name; + + /** + * The PPS type. Dir, Root or File. + * + * @var int + */ + public $Type; + + /** + * The index of the previous PPS. + * + * @var int + */ + public $PrevPps; + + /** + * The index of the next PPS. + * + * @var int + */ + public $NextPps; + + /** + * The index of it's first child if this is a Dir or Root PPS. + * + * @var int + */ + public $DirPps; + + /** + * A timestamp. + * + * @var int + */ + public $Time1st; + + /** + * A timestamp. + * + * @var int + */ + public $Time2nd; + + /** + * Starting block (small or big) for this PPS's data inside the container. + * + * @var int + */ + public $startBlock; + + /** + * The size of the PPS's data (in bytes). + * + * @var int + */ + public $Size; + + /** + * The PPS's data (only used if it's not using a temporary file). + * + * @var string + */ + public $_data; + + /** + * Array of child PPS's (only used by Root and Dir PPS's). + * + * @var array + */ + public $children = []; + + /** + * Pointer to OLE container. + * + * @var OLE + */ + public $ole; + + /** + * The constructor. + * + * @param int $No The PPS index + * @param string $name The PPS name + * @param int $type The PPS type. Dir, Root or File + * @param int $prev The index of the previous PPS + * @param int $next The index of the next PPS + * @param int $dir The index of it's first child if this is a Dir or Root PPS + * @param int $time_1st A timestamp + * @param int $time_2nd A timestamp + * @param string $data The (usually binary) source data of the PPS + * @param array $children Array containing children PPS for this PPS + */ + public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children) + { + $this->No = $No; + $this->Name = $name; + $this->Type = $type; + $this->PrevPps = $prev; + $this->NextPps = $next; + $this->DirPps = $dir; + $this->Time1st = $time_1st; + $this->Time2nd = $time_2nd; + $this->_data = $data; + $this->children = $children; + if ($data != '') { + $this->Size = strlen($data); + } else { + $this->Size = 0; + } + } + + /** + * Returns the amount of data saved for this PPS. + * + * @return int The amount of data (in bytes) + */ + public function getDataLen() + { + if (!isset($this->_data)) { + return 0; + } + + return strlen($this->_data); + } + + /** + * Returns a string with the PPS's WK (What is a WK?). + * + * @return string The binary string + */ + public function getPpsWk() + { + $ret = str_pad($this->Name, 64, "\x00"); + + $ret .= pack('v', strlen($this->Name) + 2) // 66 + . pack('c', $this->Type) // 67 + . pack('c', 0x00) //UK // 68 + . pack('V', $this->PrevPps) //Prev // 72 + . pack('V', $this->NextPps) //Next // 76 + . pack('V', $this->DirPps) //Dir // 80 + . "\x00\x09\x02\x00" // 84 + . "\x00\x00\x00\x00" // 88 + . "\xc0\x00\x00\x00" // 92 + . "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root + . "\x00\x00\x00\x00" // 100 + . OLE::localDateToOLE($this->Time1st) // 108 + . OLE::localDateToOLE($this->Time2nd) // 116 + . pack('V', isset($this->startBlock) ? $this->startBlock : 0) // 120 + . pack('V', $this->Size) // 124 + . pack('V', 0); // 128 + + return $ret; + } + + /** + * Updates index and pointers to previous, next and children PPS's for this + * PPS. I don't think it'll work with Dir PPS's. + * + * @param array &$raList Reference to the array of PPS's for the whole OLE + * container + * @param mixed $to_save + * @param mixed $depth + * + * @return int The index for this PPS + */ + public static function savePpsSetPnt(&$raList, $to_save, $depth = 0) + { + if (!is_array($to_save) || (empty($to_save))) { + return 0xFFFFFFFF; + } elseif (count($to_save) == 1) { + $cnt = count($raList); + // If the first entry, it's the root... Don't clone it! + $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0]; + $raList[$cnt]->No = $cnt; + $raList[$cnt]->PrevPps = 0xFFFFFFFF; + $raList[$cnt]->NextPps = 0xFFFFFFFF; + $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); + } else { + $iPos = floor(count($to_save) / 2); + $aPrev = array_slice($to_save, 0, $iPos); + $aNext = array_slice($to_save, $iPos + 1); + $cnt = count($raList); + // If the first entry, it's the root... Don't clone it! + $raList[$cnt] = ($depth == 0) ? $to_save[$iPos] : clone $to_save[$iPos]; + $raList[$cnt]->No = $cnt; + $raList[$cnt]->PrevPps = self::savePpsSetPnt($raList, $aPrev, $depth++); + $raList[$cnt]->NextPps = self::savePpsSetPnt($raList, $aNext, $depth++); + $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); + } + + return $cnt; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php new file mode 100644 index 00000000000..dd1cda2de03 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php @@ -0,0 +1,64 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +use PhpOffice\PhpSpreadsheet\Shared\OLE; +use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS; + +/** + * Class for creating File PPS's for OLE containers. + * + * @author Xavier Noguer + */ +class File extends PPS +{ + /** + * The constructor. + * + * @param string $name The name of the file (in Unicode) + * + * @see OLE::ascToUcs() + */ + public function __construct($name) + { + parent::__construct(null, $name, OLE::OLE_PPS_TYPE_FILE, null, null, null, null, null, '', []); + } + + /** + * Initialization method. Has to be called right after OLE_PPS_File(). + * + * @return mixed true on success + */ + public function init() + { + return true; + } + + /** + * Append data to PPS. + * + * @param string $data The data to append + */ + public function append($data): void + { + $this->_data .= $data; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php new file mode 100644 index 00000000000..5466d2bc3c8 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php @@ -0,0 +1,426 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +use PhpOffice\PhpSpreadsheet\Shared\OLE; +use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS; + +/** + * Class for creating Root PPS's for OLE containers. + * + * @author Xavier Noguer + */ +class Root extends PPS +{ + /** + * @var resource + */ + private $fileHandle; + + /** + * @var int + */ + private $smallBlockSize; + + /** + * @var int + */ + private $bigBlockSize; + + /** + * @param int $time_1st A timestamp + * @param int $time_2nd A timestamp + * @param File[] $raChild + */ + public function __construct($time_1st, $time_2nd, $raChild) + { + parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild); + } + + /** + * Method for saving the whole OLE container (including files). + * In fact, if called with an empty argument (or '-'), it saves to a + * temporary file and then outputs it's contents to stdout. + * If a resource pointer to a stream created by fopen() is passed + * it will be used, but you have to close such stream by yourself. + * + * @param resource $fileHandle the name of the file or stream where to save the OLE container + * + * @return bool true on success + */ + public function save($fileHandle) + { + $this->fileHandle = $fileHandle; + + // Initial Setting for saving + $this->bigBlockSize = 2 ** ( + (isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9 + ); + $this->smallBlockSize = 2 ** ( + (isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6 + ); + + // Make an array of PPS's (for Save) + $aList = []; + PPS::savePpsSetPnt($aList, [$this]); + // calculate values for header + [$iSBDcnt, $iBBcnt, $iPPScnt] = $this->calcSize($aList); //, $rhInfo); + // Save Header + $this->saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); + + // Make Small Data string (write SBD) + $this->_data = $this->makeSmallData($aList); + + // Write BB + $this->saveBigData($iSBDcnt, $aList); + // Write PPS + $this->savePps($aList); + // Write Big Block Depot and BDList and Adding Header informations + $this->saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); + + return true; + } + + /** + * Calculate some numbers. + * + * @param array $raList Reference to an array of PPS's + * + * @return float[] The array of numbers + */ + private function calcSize(&$raList) + { + // Calculate Basic Setting + [$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0]; + $iSmallLen = 0; + $iSBcnt = 0; + $iCount = count($raList); + for ($i = 0; $i < $iCount; ++$i) { + if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) { + $raList[$i]->Size = $raList[$i]->getDataLen(); + if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) { + $iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize) + + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0); + } else { + $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) + + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0)); + } + } + } + $iSmallLen = $iSBcnt * $this->smallBlockSize; + $iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE); + $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0); + $iBBcnt += (floor($iSmallLen / $this->bigBlockSize) + + (($iSmallLen % $this->bigBlockSize) ? 1 : 0)); + $iCnt = count($raList); + $iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE; + $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0)); + + return [$iSBDcnt, $iBBcnt, $iPPScnt]; + } + + /** + * Helper function for caculating a magic value for block sizes. + * + * @param int $i2 The argument + * + * @return float + * + * @see save() + */ + private static function adjust2($i2) + { + $iWk = log($i2) / log(2); + + return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk; + } + + /** + * Save OLE header. + * + * @param int $iSBDcnt + * @param int $iBBcnt + * @param int $iPPScnt + */ + private function saveHeader($iSBDcnt, $iBBcnt, $iPPScnt): void + { + $FILE = $this->fileHandle; + + // Calculate Basic Setting + $iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE; + $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0); + + // Calculate BD count + if ($iBdCnt > $i1stBdL) { + while (1) { + ++$iBdExL; + ++$iAllW; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0); + if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) { + break; + } + } + } + + // Save Header + fwrite( + $FILE, + "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . pack('v', 0x3b) + . pack('v', 0x03) + . pack('v', -2) + . pack('v', 9) + . pack('v', 6) + . pack('v', 0) + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . pack('V', $iBdCnt) + . pack('V', $iBBcnt + $iSBDcnt) //ROOT START + . pack('V', 0) + . pack('V', 0x1000) + . pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot + . pack('V', $iSBDcnt) + ); + // Extra BDList Start, Count + if ($iBdCnt < $i1stBdL) { + fwrite( + $FILE, + pack('V', -2) // Extra BDList Start + . pack('V', 0)// Extra BDList Count + ); + } else { + fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL)); + } + + // BDList + for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) { + fwrite($FILE, pack('V', $iAll + $i)); + } + if ($i < $i1stBdL) { + $jB = $i1stBdL - $i; + for ($j = 0; $j < $jB; ++$j) { + fwrite($FILE, (pack('V', -1))); + } + } + } + + /** + * Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL). + * + * @param int $iStBlk + * @param array &$raList Reference to array of PPS's + */ + private function saveBigData($iStBlk, &$raList): void + { + $FILE = $this->fileHandle; + + // cycle through PPS's + $iCount = count($raList); + for ($i = 0; $i < $iCount; ++$i) { + if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) { + $raList[$i]->Size = $raList[$i]->getDataLen(); + if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) { + fwrite($FILE, $raList[$i]->_data); + + if ($raList[$i]->Size % $this->bigBlockSize) { + fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize))); + } + // Set For PPS + $raList[$i]->startBlock = $iStBlk; + $iStBlk += + (floor($raList[$i]->Size / $this->bigBlockSize) + + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0)); + } + } + } + } + + /** + * get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL). + * + * @param array &$raList Reference to array of PPS's + * + * @return string + */ + private function makeSmallData(&$raList) + { + $sRes = ''; + $FILE = $this->fileHandle; + $iSmBlk = 0; + + $iCount = count($raList); + for ($i = 0; $i < $iCount; ++$i) { + // Make SBD, small data string + if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) { + if ($raList[$i]->Size <= 0) { + continue; + } + if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) { + $iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize) + + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0); + // Add to SBD + $jB = $iSmbCnt - 1; + for ($j = 0; $j < $jB; ++$j) { + fwrite($FILE, pack('V', $j + $iSmBlk + 1)); + } + fwrite($FILE, pack('V', -2)); + + // Add to Data String(this will be written for RootEntry) + $sRes .= $raList[$i]->_data; + if ($raList[$i]->Size % $this->smallBlockSize) { + $sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize)); + } + // Set for PPS + $raList[$i]->startBlock = $iSmBlk; + $iSmBlk += $iSmbCnt; + } + } + } + $iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE); + if ($iSmBlk % $iSbCnt) { + $iB = $iSbCnt - ($iSmBlk % $iSbCnt); + for ($i = 0; $i < $iB; ++$i) { + fwrite($FILE, pack('V', -1)); + } + } + + return $sRes; + } + + /** + * Saves all the PPS's WKs. + * + * @param array $raList Reference to an array with all PPS's + */ + private function savePps(&$raList): void + { + // Save each PPS WK + $iC = count($raList); + for ($i = 0; $i < $iC; ++$i) { + fwrite($this->fileHandle, $raList[$i]->getPpsWk()); + } + // Adjust for Block + $iCnt = count($raList); + $iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE; + if ($iCnt % $iBCnt) { + fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE)); + } + } + + /** + * Saving Big Block Depot. + * + * @param int $iSbdSize + * @param int $iBsize + * @param int $iPpsCnt + */ + private function saveBbd($iSbdSize, $iBsize, $iPpsCnt): void + { + $FILE = $this->fileHandle; + // Calculate Basic Setting + $iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE; + $i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBsize + $iPpsCnt + $iSbdSize; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0); + // Calculate BD count + if ($iBdCnt > $i1stBdL) { + while (1) { + ++$iBdExL; + ++$iAllW; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0); + if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) { + break; + } + } + } + + // Making BD + // Set for SBD + if ($iSbdSize > 0) { + for ($i = 0; $i < ($iSbdSize - 1); ++$i) { + fwrite($FILE, pack('V', $i + 1)); + } + fwrite($FILE, pack('V', -2)); + } + // Set for B + for ($i = 0; $i < ($iBsize - 1); ++$i) { + fwrite($FILE, pack('V', $i + $iSbdSize + 1)); + } + fwrite($FILE, pack('V', -2)); + + // Set for PPS + for ($i = 0; $i < ($iPpsCnt - 1); ++$i) { + fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1)); + } + fwrite($FILE, pack('V', -2)); + // Set for BBD itself ( 0xFFFFFFFD : BBD) + for ($i = 0; $i < $iBdCnt; ++$i) { + fwrite($FILE, pack('V', 0xFFFFFFFD)); + } + // Set for ExtraBDList + for ($i = 0; $i < $iBdExL; ++$i) { + fwrite($FILE, pack('V', 0xFFFFFFFC)); + } + // Adjust for Block + if (($iAllW + $iBdCnt) % $iBbCnt) { + $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); + for ($i = 0; $i < $iBlock; ++$i) { + fwrite($FILE, pack('V', -1)); + } + } + // Extra BDList + if ($iBdCnt > $i1stBdL) { + $iN = 0; + $iNb = 0; + for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) { + if ($iN >= ($iBbCnt - 1)) { + $iN = 0; + ++$iNb; + fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb)); + } + fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i)); + } + if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) { + $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)); + for ($i = 0; $i < $iB; ++$i) { + fwrite($FILE, pack('V', -1)); + } + } + fwrite($FILE, pack('V', -2)); + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php new file mode 100644 index 00000000000..7112b090537 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php @@ -0,0 +1,350 @@ +data = file_get_contents($pFilename, false, null, 0, 8); + + // Check OLE identifier + $identifierOle = pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1); + if ($this->data != $identifierOle) { + throw new ReaderException('The filename ' . $pFilename . ' is not recognised as an OLE file'); + } + + // Get the file data + $this->data = file_get_contents($pFilename); + + // Total number of sectors used for the SAT + $this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // SecID of the first sector of the directory stream + $this->rootStartBlock = self::getInt4d($this->data, self::ROOT_START_BLOCK_POS); + + // SecID of the first sector of the SSAT (or -2 if not extant) + $this->sbdStartBlock = self::getInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS); + + // SecID of the first sector of the MSAT (or -2 if no additional sectors are used) + $this->extensionBlock = self::getInt4d($this->data, self::EXTENSION_BLOCK_POS); + + // Total number of sectors used by MSAT + $this->numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS); + + $bigBlockDepotBlocks = []; + $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS; + + $bbdBlocks = $this->numBigBlockDepotBlocks; + + if ($this->numExtensionBlocks != 0) { + $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + } + + for ($i = 0; $i < $bbdBlocks; ++$i) { + $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos); + $pos += 4; + } + + for ($j = 0; $j < $this->numExtensionBlocks; ++$j) { + $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE; + $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1); + + for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) { + $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos); + $pos += 4; + } + + $bbdBlocks += $blocksToRead; + if ($bbdBlocks < $this->numBigBlockDepotBlocks) { + $this->extensionBlock = self::getInt4d($this->data, $pos); + } + } + + $pos = 0; + $this->bigBlockChain = ''; + $bbs = self::BIG_BLOCK_SIZE / 4; + for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) { + $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE; + + $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs); + $pos += 4 * $bbs; + } + + $pos = 0; + $sbdBlock = $this->sbdStartBlock; + $this->smallBlockChain = ''; + while ($sbdBlock != -2) { + $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE; + + $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs); + $pos += 4 * $bbs; + + $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4); + } + + // read the directory stream + $block = $this->rootStartBlock; + $this->entry = $this->readData($block); + + $this->readPropertySets(); + } + + /** + * Extract binary stream data. + * + * @param int $stream + * + * @return string + */ + public function getStream($stream) + { + if ($stream === null) { + return null; + } + + $streamData = ''; + + if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) { + $rootdata = $this->readData($this->props[$this->rootentry]['startBlock']); + + $block = $this->props[$stream]['startBlock']; + + while ($block != -2) { + $pos = $block * self::SMALL_BLOCK_SIZE; + $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE); + + $block = self::getInt4d($this->smallBlockChain, $block * 4); + } + + return $streamData; + } + $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE; + if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) { + ++$numBlocks; + } + + if ($numBlocks == 0) { + return ''; + } + + $block = $this->props[$stream]['startBlock']; + + while ($block != -2) { + $pos = ($block + 1) * self::BIG_BLOCK_SIZE; + $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE); + $block = self::getInt4d($this->bigBlockChain, $block * 4); + } + + return $streamData; + } + + /** + * Read a standard stream (by joining sectors using information from SAT). + * + * @param int $bl Sector ID where the stream starts + * + * @return string Data for standard stream + */ + private function readData($bl) + { + $block = $bl; + $data = ''; + + while ($block != -2) { + $pos = ($block + 1) * self::BIG_BLOCK_SIZE; + $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE); + $block = self::getInt4d($this->bigBlockChain, $block * 4); + } + + return $data; + } + + /** + * Read entries in the directory stream. + */ + private function readPropertySets(): void + { + $offset = 0; + + // loop through entires, each entry is 128 bytes + $entryLen = strlen($this->entry); + while ($offset < $entryLen) { + // entry data (128 bytes) + $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE); + + // size in bytes of name + $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8); + + // type of entry + $type = ord($d[self::TYPE_POS]); + + // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook) + // sectorID of first sector of the short-stream container stream, if this entry is root entry + $startBlock = self::getInt4d($d, self::START_BLOCK_POS); + + $size = self::getInt4d($d, self::SIZE_POS); + + $name = str_replace("\x00", '', substr($d, 0, $nameSize)); + + $this->props[] = [ + 'name' => $name, + 'type' => $type, + 'startBlock' => $startBlock, + 'size' => $size, + ]; + + // tmp helper to simplify checks + $upName = strtoupper($name); + + // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook) + if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) { + $this->wrkbook = count($this->props) - 1; + } elseif ($upName === 'ROOT ENTRY' || $upName === 'R') { + // Root entry + $this->rootentry = count($this->props) - 1; + } + + // Summary information + if ($name == chr(5) . 'SummaryInformation') { + $this->summaryInformation = count($this->props) - 1; + } + + // Additional Document Summary information + if ($name == chr(5) . 'DocumentSummaryInformation') { + $this->documentSummaryInformation = count($this->props) - 1; + } + + $offset += self::PROPERTY_STORAGE_BLOCK_SIZE; + } + } + + /** + * Read 4 bytes of data at specified position. + * + * @param string $data + * @param int $pos + * + * @return int + */ + private static function getInt4d($data, $pos) + { + if ($pos < 0) { + // Invalid position + throw new ReaderException('Parameter pos=' . $pos . ' is invalid.'); + } + + $len = strlen($data); + if ($len < $pos + 4) { + $data .= str_repeat("\0", $pos + 4 - $len); + } + + // FIX: represent numbers correctly on 64-bit system + // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 + // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems + $_or_24 = ord($data[$pos + 3]); + if ($_or_24 >= 128) { + // negative number + $_ord_24 = -abs((256 - $_or_24) << 24); + } else { + $_ord_24 = ($_or_24 & 127) << 24; + } + + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php index 9b0080b9dbf..9fefe88fb5e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php @@ -2,8 +2,41 @@ namespace PhpOffice\PhpSpreadsheet\Shared; +use PhpOffice\PhpSpreadsheet\Exception; +use PhpOffice\PhpSpreadsheet\Worksheet\Protection; + class PasswordHasher { + /** + * Get algorithm name for PHP. + */ + private static function getAlgorithm(string $algorithmName): string + { + if (!$algorithmName) { + return ''; + } + + // Mapping between algorithm name in Excel and algorithm name in PHP + $mapping = [ + Protection::ALGORITHM_MD2 => 'md2', + Protection::ALGORITHM_MD4 => 'md4', + Protection::ALGORITHM_MD5 => 'md5', + Protection::ALGORITHM_SHA_1 => 'sha1', + Protection::ALGORITHM_SHA_256 => 'sha256', + Protection::ALGORITHM_SHA_384 => 'sha384', + Protection::ALGORITHM_SHA_512 => 'sha512', + Protection::ALGORITHM_RIPEMD_128 => 'ripemd128', + Protection::ALGORITHM_RIPEMD_160 => 'ripemd160', + Protection::ALGORITHM_WHIRLPOOL => 'whirlpool', + ]; + + if (array_key_exists($algorithmName, $mapping)) { + return $mapping[$algorithmName]; + } + + throw new Exception('Unsupported password algorithm: ' . $algorithmName); + } + /** * Create a password hash from a given string. * @@ -12,10 +45,8 @@ class PasswordHasher * Spreadsheet_Excel_Writer by Xavier Noguer . * * @param string $pPassword Password to hash - * - * @return string Hashed password */ - public static function hashPassword($pPassword) + private static function defaultHashPassword(string $pPassword): string { $password = 0x0000; $charPos = 1; // char position @@ -34,4 +65,36 @@ class PasswordHasher return strtoupper(dechex($password)); } + + /** + * Create a password hash from a given string by a specific algorithm. + * + * 2.4.2.4 ISO Write Protection Method + * + * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f + * + * @param string $password Password to hash + * @param string $algorithm Hash algorithm used to compute the password hash value + * @param string $salt Pseudorandom string + * @param int $spinCount Number of times to iterate on a hash of a password + * + * @return string Hashed password + */ + public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string + { + $phpAlgorithm = self::getAlgorithm($algorithm); + if (!$phpAlgorithm) { + return self::defaultHashPassword($password); + } + + $saltValue = base64_decode($salt); + $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8'); + + $hashValue = hash($phpAlgorithm, $saltValue . $encodedPassword, true); + for ($i = 0; $i < $spinCount; ++$i) { + $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true); + } + + return base64_encode($hashValue); + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php index d949203babc..9ae324132d1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php @@ -63,7 +63,7 @@ class StringHelper /** * Build control characters array. */ - private static function buildControlCharacters() + private static function buildControlCharacters(): void { for ($i = 0; $i <= 31; ++$i) { if ($i != 9 && $i != 10 && $i != 13) { @@ -77,7 +77,7 @@ class StringHelper /** * Build SYLK characters array. */ - private static function buildSYLKCharacters() + private static function buildSYLKCharacters(): void { self::$SYLKCharacters = [ "\x1B 0" => chr(0), @@ -272,7 +272,7 @@ class StringHelper return self::$isIconvEnabled; } - private static function buildCharacterSets() + private static function buildCharacterSets(): void { if (empty(self::$controlCharacters)) { self::buildControlCharacters(); @@ -603,7 +603,7 @@ class StringHelper * * @param string $pValue Character for decimal separator */ - public static function setDecimalSeparator($pValue) + public static function setDecimalSeparator($pValue): void { self::$decimalSeparator = $pValue; } @@ -636,7 +636,7 @@ class StringHelper * * @param string $pValue Character for thousands separator */ - public static function setThousandsSeparator($pValue) + public static function setThousandsSeparator($pValue): void { self::$thousandsSeparator = $pValue; } @@ -674,7 +674,7 @@ class StringHelper * * @param string $pValue Character for currency code */ - public static function setCurrencyCode($pValue) + public static function setCurrencyCode($pValue): void { self::$currencyCode = $pValue; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php index e5a99b9b6b8..43fd365369f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php @@ -23,7 +23,7 @@ class TimeZone */ private static function validateTimeZone($timezone) { - return in_array($timezone, DateTimeZone::listIdentifiers()); + return in_array($timezone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC)); } /** @@ -61,8 +61,6 @@ class TimeZone * @param string $timezone The timezone for finding the adjustment to UST * @param int $timestamp PHP date/time value * - * @throws PhpSpreadsheetException - * * @return int Number of seconds for timezone adjustment */ public static function getTimeZoneAdjustment($timezone, $timestamp) @@ -75,10 +73,6 @@ class TimeZone $timezone = self::$timezone; } - if ($timezone == 'UST') { - return 0; - } - $objTimezone = new DateTimeZone($timezone); $transitions = $objTimezone->getTransitions($timestamp, $timestamp); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php index d8e63d5e289..c94997226bd 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -341,7 +341,7 @@ class BestFit return $this->yBestFitValues; } - protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const) + protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void { $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0; foreach ($this->xValues as $xKey => $xValue) { @@ -377,7 +377,7 @@ class BestFit $this->SSRegression = $this->goodnessOfFit * $SStot; $this->covariance = $SScov / $this->valueCount; - $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - pow($sumX, 2)) * ($this->valueCount * $sumY2 - pow($sumY, 2))); + $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2)); $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex); $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2)); if ($this->SSResiduals != 0.0) { @@ -400,7 +400,7 @@ class BestFit * @param float[] $xValues * @param bool $const */ - protected function leastSquareFit(array $yValues, array $xValues, $const) + protected function leastSquareFit(array $yValues, array $xValues, $const): void { // calculate sums $x_sum = array_sum($xValues); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php index 5b57f4b71fe..82866dee80d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php @@ -21,7 +21,7 @@ class ExponentialBestFit extends BestFit */ public function getValueOfYForX($xValue) { - return $this->getIntersect() * pow($this->getSlope(), ($xValue - $this->xOffset)); + return $this->getIntersect() * $this->getSlope() ** ($xValue - $this->xOffset); } /** @@ -90,7 +90,7 @@ class ExponentialBestFit extends BestFit * @param float[] $xValues The set of X-values for this regression * @param bool $const */ - private function exponentialRegression($yValues, $xValues, $const) + private function exponentialRegression($yValues, $xValues, $const): void { foreach ($yValues as &$value) { if ($value < 0.0) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php index 217f0964770..26a562c5516 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php @@ -58,7 +58,7 @@ class LinearBestFit extends BestFit * @param float[] $xValues The set of X-values for this regression * @param bool $const */ - private function linearRegression($yValues, $xValues, $const) + private function linearRegression($yValues, $xValues, $const): void { $this->leastSquareFit($yValues, $xValues, $const); } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php index 96ca2ed8794..c469067d47c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php @@ -58,7 +58,7 @@ class LogarithmicBestFit extends BestFit * @param float[] $xValues The set of X-values for this regression * @param bool $const */ - private function logarithmicRegression($yValues, $xValues, $const) + private function logarithmicRegression($yValues, $xValues, $const): void { foreach ($xValues as &$value) { if ($value < 0.0) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php index a1510491f73..d959eddb751 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php @@ -44,7 +44,7 @@ class PolynomialBestFit extends BestFit $slope = $this->getSlope(); foreach ($slope as $key => $value) { if ($value != 0.0) { - $retVal += $value * pow($xValue, $key + 1); + $retVal += $value * $xValue ** ($key + 1); } } @@ -121,7 +121,7 @@ class PolynomialBestFit extends BestFit * @param float[] $yValues The set of Y-values for this regression * @param float[] $xValues The set of X-values for this regression */ - private function polynomialRegression($order, $yValues, $xValues) + private function polynomialRegression($order, $yValues, $xValues): void { // calculate sums $x_sum = array_sum($xValues); @@ -144,7 +144,7 @@ class PolynomialBestFit extends BestFit $B = []; for ($i = 0; $i < $this->valueCount; ++$i) { for ($j = 0; $j <= $order; ++$j) { - $A[$i][$j] = pow($xValues[$i], $j); + $A[$i][$j] = $xValues[$i] ** $j; } } for ($i = 0; $i < $this->valueCount; ++$i) { @@ -157,7 +157,7 @@ class PolynomialBestFit extends BestFit $coefficients = []; for ($i = 0; $i < $C->getRowDimension(); ++$i) { $r = $C->get($i, 0); - if (abs($r) <= pow(10, -9)) { + if (abs($r) <= 10 ** (-9)) { $r = 0; } $coefficients[] = $r; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php index 4eefec8265c..c53eab6381d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php @@ -21,7 +21,7 @@ class PowerBestFit extends BestFit */ public function getValueOfYForX($xValue) { - return $this->getIntersect() * pow(($xValue - $this->xOffset), $this->getSlope()); + return $this->getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope(); } /** @@ -33,7 +33,7 @@ class PowerBestFit extends BestFit */ public function getValueOfXForY($yValue) { - return pow((($yValue + $this->yOffset) / $this->getIntersect()), (1 / $this->getSlope())); + return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope()); } /** @@ -74,7 +74,7 @@ class PowerBestFit extends BestFit * @param float[] $xValues The set of X-values for this regression * @param bool $const */ - private function powerRegression($yValues, $xValues, $const) + private function powerRegression($yValues, $xValues, $const): void { foreach ($xValues as &$value) { if ($value < 0.0) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php new file mode 100644 index 00000000000..c9eaf378b63 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php @@ -0,0 +1,279 @@ +getParent()->getDefaultStyle()->getFont(); + + $columnDimensions = $sheet->getColumnDimensions(); + + // first find the true column width in pixels (uncollapsed and unhidden) + if (isset($columnDimensions[$col]) && $columnDimensions[$col]->getWidth() != -1) { + // then we have column dimension with explicit width + $columnDimension = $columnDimensions[$col]; + $width = $columnDimension->getWidth(); + $pixelWidth = Drawing::cellDimensionToPixels($width, $font); + } elseif ($sheet->getDefaultColumnDimension()->getWidth() != -1) { + // then we have default column dimension with explicit width + $defaultColumnDimension = $sheet->getDefaultColumnDimension(); + $width = $defaultColumnDimension->getWidth(); + $pixelWidth = Drawing::cellDimensionToPixels($width, $font); + } else { + // we don't even have any default column dimension. Width depends on default font + $pixelWidth = Font::getDefaultColumnWidthByFont($font, true); + } + + // now find the effective column width in pixels + if (isset($columnDimensions[$col]) && !$columnDimensions[$col]->getVisible()) { + $effectivePixelWidth = 0; + } else { + $effectivePixelWidth = $pixelWidth; + } + + return $effectivePixelWidth; + } + + /** + * Convert the height of a cell from user's units to pixels. By interpolation + * the relationship is: y = 4/3x. If the height hasn't been set by the user we + * use the default value. If the row is hidden we use a value of zero. + * + * @param Worksheet $sheet The sheet + * @param int $row The row index (1-based) + * + * @return int The width in pixels + */ + public static function sizeRow($sheet, $row = 1) + { + // default font of the workbook + $font = $sheet->getParent()->getDefaultStyle()->getFont(); + + $rowDimensions = $sheet->getRowDimensions(); + + // first find the true row height in pixels (uncollapsed and unhidden) + if (isset($rowDimensions[$row]) && $rowDimensions[$row]->getRowHeight() != -1) { + // then we have a row dimension + $rowDimension = $rowDimensions[$row]; + $rowHeight = $rowDimension->getRowHeight(); + $pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10 + } elseif ($sheet->getDefaultRowDimension()->getRowHeight() != -1) { + // then we have a default row dimension with explicit height + $defaultRowDimension = $sheet->getDefaultRowDimension(); + $rowHeight = $defaultRowDimension->getRowHeight(); + $pixelRowHeight = Drawing::pointsToPixels($rowHeight); + } else { + // we don't even have any default row dimension. Height depends on default font + $pointRowHeight = Font::getDefaultRowHeightByFont($font); + $pixelRowHeight = Font::fontSizeToPixels($pointRowHeight); + } + + // now find the effective row height in pixels + if (isset($rowDimensions[$row]) && !$rowDimensions[$row]->getVisible()) { + $effectivePixelRowHeight = 0; + } else { + $effectivePixelRowHeight = $pixelRowHeight; + } + + return $effectivePixelRowHeight; + } + + /** + * Get the horizontal distance in pixels between two anchors + * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets. + * + * @param string $startColumn + * @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width + * @param string $endColumn + * @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width + * + * @return int Horizontal measured in pixels + */ + public static function getDistanceX(Worksheet $sheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0) + { + $distanceX = 0; + + // add the widths of the spanning columns + $startColumnIndex = Coordinate::columnIndexFromString($startColumn); + $endColumnIndex = Coordinate::columnIndexFromString($endColumn); + for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) { + $distanceX += self::sizeCol($sheet, Coordinate::stringFromColumnIndex($i)); + } + + // correct for offsetX in startcell + $distanceX -= (int) floor(self::sizeCol($sheet, $startColumn) * $startOffsetX / 1024); + + // correct for offsetX in endcell + $distanceX -= (int) floor(self::sizeCol($sheet, $endColumn) * (1 - $endOffsetX / 1024)); + + return $distanceX; + } + + /** + * Get the vertical distance in pixels between two anchors + * The distanceY is found as sum of all the spanning rows minus two offsets. + * + * @param int $startRow (1-based) + * @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height + * @param int $endRow (1-based) + * @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height + * + * @return int Vertical distance measured in pixels + */ + public static function getDistanceY(Worksheet $sheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0) + { + $distanceY = 0; + + // add the widths of the spanning rows + for ($row = $startRow; $row <= $endRow; ++$row) { + $distanceY += self::sizeRow($sheet, $row); + } + + // correct for offsetX in startcell + $distanceY -= (int) floor(self::sizeRow($sheet, $startRow) * $startOffsetY / 256); + + // correct for offsetX in endcell + $distanceY -= (int) floor(self::sizeRow($sheet, $endRow) * (1 - $endOffsetY / 256)); + + return $distanceY; + } + + /** + * Convert 1-cell anchor coordinates to 2-cell anchor coordinates + * This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications. + * + * Calculate the vertices that define the position of the image as required by + * the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be taken into + * account. + * The values of $col_start and $row_start are passed in from the calling + * function. The values of $col_end and $row_end are calculated by subtracting + * the width and height of the bitmap from the width and height of the + * underlying cells. + * The vertices are expressed as a percentage of the underlying cell width as + * follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * + * @param Worksheet $sheet + * @param string $coordinates E.g. 'A1' + * @param int $offsetX Horizontal offset in pixels + * @param int $offsetY Vertical offset in pixels + * @param int $width Width in pixels + * @param int $height Height in pixels + * + * @return array + */ + public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height) + { + [$column, $row] = Coordinate::coordinateFromString($coordinates); + $col_start = Coordinate::columnIndexFromString($column); + $row_start = $row - 1; + + $x1 = $offsetX; + $y1 = $offsetY; + + // Initialise end cell to the same as the start cell + $col_end = $col_start; // Col containing lower right corner of object + $row_end = $row_start; // Row containing bottom right corner of object + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start))) { + $x1 = 0; + } + if ($y1 >= self::sizeRow($sheet, $row_start + 1)) { + $y1 = 0; + } + + $width = $width + $x1 - 1; + $height = $height + $y1 - 1; + + // Subtract the underlying cell widths to find the end cell of the image + while ($width >= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end))) { + $width -= self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end)); + ++$col_end; + } + + // Subtract the underlying cell heights to find the end cell of the image + while ($height >= self::sizeRow($sheet, $row_end + 1)) { + $height -= self::sizeRow($sheet, $row_end + 1); + ++$row_end; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + // with zero height or width. + if (self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start)) == 0) { + return; + } + if (self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end)) == 0) { + return; + } + if (self::sizeRow($sheet, $row_start + 1) == 0) { + return; + } + if (self::sizeRow($sheet, $row_end + 1) == 0) { + return; + } + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_start)) * 1024; + $y1 = $y1 / self::sizeRow($sheet, $row_start + 1) * 256; + $x2 = ($width + 1) / self::sizeCol($sheet, Coordinate::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object + $y2 = ($height + 1) / self::sizeRow($sheet, $row_end + 1) * 256; // Distance to bottom of object + + $startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1); + $endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1); + + return [ + 'startCoordinates' => $startCoordinates, + 'startOffsetX' => $x1, + 'startOffsetY' => $y1, + 'endCoordinates' => $endCoordinates, + 'endOffsetX' => $x2, + 'endOffsetY' => $y2, + ]; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php index 04a9f1df832..c8e8f72cfa6 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\Iterator; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; @@ -14,6 +15,9 @@ class Spreadsheet const VISIBILITY_HIDDEN = 'hidden'; const VISIBILITY_VERY_HIDDEN = 'veryHidden'; + private const DEFINED_NAME_IS_RANGE = false; + private const DEFINED_NAME_IS_FORMULA = true; + private static $workbookViewVisibilityValues = [ self::VISIBILITY_VISIBLE, self::VISIBILITY_HIDDEN, @@ -67,7 +71,7 @@ class Spreadsheet * * @var NamedRange[] */ - private $namedRanges = []; + private $definedNames = []; /** * CellXf supervisor. @@ -210,7 +214,7 @@ class Spreadsheet * * @param bool $hasMacros true|false */ - public function setHasMacros($hasMacros) + public function setHasMacros($hasMacros): void { $this->hasMacros = (bool) $hasMacros; } @@ -220,7 +224,7 @@ class Spreadsheet * * @param string $macroCode string|null */ - public function setMacrosCode($macroCode) + public function setMacrosCode($macroCode): void { $this->macrosCode = $macroCode; $this->setHasMacros($macroCode !== null); @@ -241,7 +245,7 @@ class Spreadsheet * * @param null|string $certificate */ - public function setMacrosCertificate($certificate) + public function setMacrosCertificate($certificate): void { $this->macrosCertificate = $certificate; } @@ -269,7 +273,7 @@ class Spreadsheet /** * Remove all macros, certificate from spreadsheet. */ - public function discardMacros() + public function discardMacros(): void { $this->hasMacros = false; $this->macrosCode = null; @@ -282,7 +286,7 @@ class Spreadsheet * @param null|mixed $target * @param null|mixed $xmlData */ - public function setRibbonXMLData($target, $xmlData) + public function setRibbonXMLData($target, $xmlData): void { if ($target !== null && $xmlData !== null) { $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData]; @@ -327,7 +331,7 @@ class Spreadsheet * @param null|mixed $BinObjectsNames * @param null|mixed $BinObjectsData */ - public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData) + public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void { if ($BinObjectsNames !== null && $BinObjectsData !== null) { $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData]; @@ -354,10 +358,8 @@ class Spreadsheet * It has to be minimized when the library start to support currently unparsed data. * * @internal - * - * @param array $unparsedLoadedData */ - public function setUnparsedLoadedData(array $unparsedLoadedData) + public function setUnparsedLoadedData(array $unparsedLoadedData): void { $this->unparsedLoadedData = $unparsedLoadedData; } @@ -398,8 +400,10 @@ class Spreadsheet break; case 'types': - if (is_array($this->ribbonBinObjects) && - isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])) { + if ( + is_array($this->ribbonBinObjects) && + isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data']) + ) { $tmpTypes = array_keys($this->ribbonBinObjects['data']); $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes)); } else { @@ -482,8 +486,8 @@ class Spreadsheet // Create document security $this->security = new Document\Security(); - // Set named ranges - $this->namedRanges = []; + // Set defined names + $this->definedNames = []; // Create the cellXf supervisor $this->cellXfSupervisor = new Style(true); @@ -507,7 +511,7 @@ class Spreadsheet * Disconnect all worksheets from this PhpSpreadsheet workbook object, * typically so that the PhpSpreadsheet object can be unset. */ - public function disconnectWorksheets() + public function disconnectWorksheets(): void { $worksheet = null; foreach ($this->workSheetCollection as $k => &$worksheet) { @@ -540,10 +544,8 @@ class Spreadsheet /** * Set properties. - * - * @param Document\Properties $pValue */ - public function setProperties(Document\Properties $pValue) + public function setProperties(Document\Properties $pValue): void { $this->properties = $pValue; } @@ -560,10 +562,8 @@ class Spreadsheet /** * Set security. - * - * @param Document\Security $pValue */ - public function setSecurity(Document\Security $pValue) + public function setSecurity(Document\Security $pValue): void { $this->security = $pValue; } @@ -571,8 +571,6 @@ class Spreadsheet /** * Get active sheet. * - * @throws Exception - * * @return Worksheet */ public function getActiveSheet() @@ -585,8 +583,6 @@ class Spreadsheet * * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last) * - * @throws Exception - * * @return Worksheet */ public function createSheet($sheetIndex = null) @@ -612,11 +608,8 @@ class Spreadsheet /** * Add sheet. * - * @param Worksheet $pSheet * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last) * - * @throws Exception - * * @return Worksheet */ public function addSheet(Worksheet $pSheet, $iSheetIndex = null) @@ -658,10 +651,8 @@ class Spreadsheet * Remove sheet by index. * * @param int $pIndex Active sheet index - * - * @throws Exception */ - public function removeSheetByIndex($pIndex) + public function removeSheetByIndex($pIndex): void { $numSheets = count($this->workSheetCollection); if ($pIndex > $numSheets - 1) { @@ -672,8 +663,10 @@ class Spreadsheet array_splice($this->workSheetCollection, $pIndex, 1); // Adjust active sheet index if necessary - if (($this->activeSheetIndex >= $pIndex) && - ($pIndex > count($this->workSheetCollection) - 1)) { + if ( + ($this->activeSheetIndex >= $pIndex) && + ($this->activeSheetIndex > 0 || $numSheets <= 1) + ) { --$this->activeSheetIndex; } } @@ -683,8 +676,6 @@ class Spreadsheet * * @param int $pIndex Sheet index * - * @throws Exception - * * @return Worksheet */ public function getSheet($pIndex) @@ -732,16 +723,12 @@ class Spreadsheet /** * Get index for sheet. * - * @param Worksheet $pSheet - * - * @throws Exception - * * @return int index */ public function getIndex(Worksheet $pSheet) { foreach ($this->workSheetCollection as $key => $value) { - if ($value->getHashCode() == $pSheet->getHashCode()) { + if ($value->getHashCode() === $pSheet->getHashCode()) { return $key; } } @@ -755,8 +742,6 @@ class Spreadsheet * @param string $sheetName Sheet name to modify index for * @param int $newIndex New index for the sheet * - * @throws Exception - * * @return int New sheet index */ public function setIndexByName($sheetName, $newIndex) @@ -802,8 +787,6 @@ class Spreadsheet * * @param int $pIndex Active sheet index * - * @throws Exception - * * @return Worksheet */ public function setActiveSheetIndex($pIndex) @@ -825,8 +808,6 @@ class Spreadsheet * * @param string $pValue Sheet title * - * @throws Exception - * * @return Worksheet */ public function setActiveSheetIndexByName($pValue) @@ -862,8 +843,6 @@ class Spreadsheet * @param Worksheet $pSheet External sheet to add * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last) * - * @throws Exception - * * @return Worksheet */ public function addExternalSheet(Worksheet $pSheet, $iSheetIndex = null) @@ -893,56 +872,159 @@ class Spreadsheet } /** - * Get named ranges. + * Get an array of all Named Ranges. * * @return NamedRange[] */ - public function getNamedRanges() + public function getNamedRanges(): array { - return $this->namedRanges; + return array_filter( + $this->definedNames, + function (DefinedName $definedName) { + return $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE; + } + ); } /** - * Add named range. + * Get an array of all Named Formulae. * - * @param NamedRange $namedRange - * - * @return bool + * @return NamedFormula[] */ - public function addNamedRange(NamedRange $namedRange) + public function getNamedFormulae(): array { - if ($namedRange->getScope() == null) { + return array_filter( + $this->definedNames, + function (DefinedName $definedName) { + return $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA; + } + ); + } + + /** + * Get an array of all Defined Names (both named ranges and named formulae). + * + * @return DefinedName[] + */ + public function getDefinedNames(): array + { + return $this->definedNames; + } + + /** + * Add a named range. + * If a named range with this name already exists, then this will replace the existing value. + */ + public function addNamedRange(NamedRange $namedRange): void + { + $this->addDefinedName($namedRange); + } + + /** + * Add a named formula. + * If a named formula with this name already exists, then this will replace the existing value. + */ + public function addNamedFormula(NamedFormula $namedFormula): void + { + $this->addDefinedName($namedFormula); + } + + /** + * Add a defined name (either a named range or a named formula). + * If a defined named with this name already exists, then this will replace the existing value. + */ + public function addDefinedName(DefinedName $definedName): void + { + $upperCaseName = StringHelper::strToUpper($definedName->getName()); + if ($definedName->getScope() == null) { // global scope - $this->namedRanges[$namedRange->getName()] = $namedRange; + $this->definedNames[$upperCaseName] = $definedName; } else { // local scope - $this->namedRanges[$namedRange->getScope()->getTitle() . '!' . $namedRange->getName()] = $namedRange; + $this->definedNames[$definedName->getScope()->getTitle() . '!' . $upperCaseName] = $definedName; } - - return true; } /** * Get named range. * - * @param string $namedRange * @param null|Worksheet $pSheet Scope. Use null for global scope - * - * @return null|NamedRange */ - public function getNamedRange($namedRange, Worksheet $pSheet = null) + public function getNamedRange(string $namedRange, ?Worksheet $pSheet = null): ?NamedRange { $returnValue = null; - if ($namedRange != '' && ($namedRange !== null)) { + if ($namedRange !== '') { + $namedRange = StringHelper::strToUpper($namedRange); + // first look for global named range + $returnValue = $this->getGlobalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE); + // then look for local named range (has priority over global named range if both names exist) + $returnValue = $this->getLocalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE, $pSheet) ?: $returnValue; + } + + return $returnValue instanceof NamedRange ? $returnValue : null; + } + + /** + * Get named formula. + * + * @param null|Worksheet $pSheet Scope. Use null for global scope + */ + public function getNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): ?NamedFormula + { + $returnValue = null; + + if ($namedFormula !== '') { + $namedFormula = StringHelper::strToUpper($namedFormula); + // first look for global named formula + $returnValue = $this->getGlobalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA); + // then look for local named formula (has priority over global named formula if both names exist) + $returnValue = $this->getLocalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA, $pSheet) ?: $returnValue; + } + + return $returnValue instanceof NamedFormula ? $returnValue : null; + } + + private function getGlobalDefinedNameByType(string $name, bool $type): ?DefinedName + { + if (isset($this->definedNames[$name]) && $this->definedNames[$name]->isFormula() === $type) { + return $this->definedNames[$name]; + } + + return null; + } + + private function getLocalDefinedNameByType(string $name, bool $type, ?Worksheet $pSheet = null): ?DefinedName + { + if ( + ($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $name]) + && $this->definedNames[$pSheet->getTitle() . '!' . $name]->isFormula() === $type + ) { + return $this->definedNames[$pSheet->getTitle() . '!' . $name]; + } + + return null; + } + + /** + * Get named range. + * + * @param null|Worksheet $pSheet Scope. Use null for global scope + */ + public function getDefinedName(string $definedName, ?Worksheet $pSheet = null): ?DefinedName + { + $returnValue = null; + + if ($definedName !== '') { + $definedName = StringHelper::strToUpper($definedName); // first look for global defined name - if (isset($this->namedRanges[$namedRange])) { - $returnValue = $this->namedRanges[$namedRange]; + if (isset($this->definedNames[$definedName])) { + $returnValue = $this->definedNames[$definedName]; } // then look for local defined name (has priority over global defined name if both names exist) - if (($pSheet !== null) && isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) { - $returnValue = $this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]; + if (($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) { + $returnValue = $this->definedNames[$pSheet->getTitle() . '!' . $definedName]; } } @@ -952,20 +1034,55 @@ class Spreadsheet /** * Remove named range. * - * @param string $namedRange * @param null|Worksheet $pSheet scope: use null for global scope * - * @return Spreadsheet + * @return $this */ - public function removeNamedRange($namedRange, Worksheet $pSheet = null) + public function removeNamedRange(string $namedRange, ?Worksheet $pSheet = null): self { + if ($this->getNamedRange($namedRange, $pSheet) === null) { + return $this; + } + + return $this->removeDefinedName($namedRange, $pSheet); + } + + /** + * Remove named formula. + * + * @param null|Worksheet $pSheet scope: use null for global scope + * + * @return $this + */ + public function removeNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): self + { + if ($this->getNamedFormula($namedFormula, $pSheet) === null) { + return $this; + } + + return $this->removeDefinedName($namedFormula, $pSheet); + } + + /** + * Remove defined name. + * + * @param null|Worksheet $pSheet scope: use null for global scope + * + * @return $this + */ + public function removeDefinedName(string $definedName, ?Worksheet $pSheet = null): self + { + $definedName = StringHelper::strToUpper($definedName); + if ($pSheet === null) { - if (isset($this->namedRanges[$namedRange])) { - unset($this->namedRanges[$namedRange]); + if (isset($this->definedNames[$definedName])) { + unset($this->definedNames[$definedName]); } } else { - if (isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) { - unset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]); + if (isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) { + unset($this->definedNames[$pSheet->getTitle() . '!' . $definedName]); + } elseif (isset($this->definedNames[$definedName])) { + unset($this->definedNames[$definedName]); } } @@ -1044,7 +1161,7 @@ class Spreadsheet public function getCellXfByHashCode($pValue) { foreach ($this->cellXfCollection as $cellXf) { - if ($cellXf->getHashCode() == $pValue) { + if ($cellXf->getHashCode() === $pValue) { return $cellXf; } } @@ -1067,8 +1184,6 @@ class Spreadsheet /** * Get default style. * - * @throws Exception - * * @return Style */ public function getDefaultStyle() @@ -1082,10 +1197,8 @@ class Spreadsheet /** * Add a cellXf to the workbook. - * - * @param Style $style */ - public function addCellXf(Style $style) + public function addCellXf(Style $style): void { $this->cellXfCollection[] = $style; $style->setIndex(count($this->cellXfCollection) - 1); @@ -1095,10 +1208,8 @@ class Spreadsheet * Remove cellXf by index. It is ensured that all cells get their xf index updated. * * @param int $pIndex Index to cellXf - * - * @throws Exception */ - public function removeCellXfByIndex($pIndex) + public function removeCellXfByIndex($pIndex): void { if ($pIndex > count($this->cellXfCollection) - 1) { throw new Exception('CellXf index is out of bounds.'); @@ -1165,7 +1276,7 @@ class Spreadsheet public function getCellStyleXfByHashCode($pValue) { foreach ($this->cellStyleXfCollection as $cellStyleXf) { - if ($cellStyleXf->getHashCode() == $pValue) { + if ($cellStyleXf->getHashCode() === $pValue) { return $cellStyleXf; } } @@ -1175,10 +1286,8 @@ class Spreadsheet /** * Add a cellStyleXf to the workbook. - * - * @param Style $pStyle */ - public function addCellStyleXf(Style $pStyle) + public function addCellStyleXf(Style $pStyle): void { $this->cellStyleXfCollection[] = $pStyle; $pStyle->setIndex(count($this->cellStyleXfCollection) - 1); @@ -1188,10 +1297,8 @@ class Spreadsheet * Remove cellStyleXf by index. * * @param int $pIndex Index to cellXf - * - * @throws Exception */ - public function removeCellStyleXfByIndex($pIndex) + public function removeCellStyleXfByIndex($pIndex): void { if ($pIndex > count($this->cellStyleXfCollection) - 1) { throw new Exception('CellStyleXf index is out of bounds.'); @@ -1203,7 +1310,7 @@ class Spreadsheet * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells * and columns in the workbook. */ - public function garbageCollect() + public function garbageCollect(): void { // how many references are there to each cellXf ? $countReferencesCellXf = []; @@ -1304,7 +1411,7 @@ class Spreadsheet * * @param bool $showHorizontalScroll True if horizonal scroll bar is visible */ - public function setShowHorizontalScroll($showHorizontalScroll) + public function setShowHorizontalScroll($showHorizontalScroll): void { $this->showHorizontalScroll = (bool) $showHorizontalScroll; } @@ -1324,7 +1431,7 @@ class Spreadsheet * * @param bool $showVerticalScroll True if vertical scroll bar is visible */ - public function setShowVerticalScroll($showVerticalScroll) + public function setShowVerticalScroll($showVerticalScroll): void { $this->showVerticalScroll = (bool) $showVerticalScroll; } @@ -1344,7 +1451,7 @@ class Spreadsheet * * @param bool $showSheetTabs True if sheet tabs are visible */ - public function setShowSheetTabs($showSheetTabs) + public function setShowSheetTabs($showSheetTabs): void { $this->showSheetTabs = (bool) $showSheetTabs; } @@ -1364,7 +1471,7 @@ class Spreadsheet * * @param bool $minimized true if workbook window is minimized */ - public function setMinimized($minimized) + public function setMinimized($minimized): void { $this->minimized = (bool) $minimized; } @@ -1386,7 +1493,7 @@ class Spreadsheet * * @param bool $autoFilterDateGrouping true if workbook window is minimized */ - public function setAutoFilterDateGrouping($autoFilterDateGrouping) + public function setAutoFilterDateGrouping($autoFilterDateGrouping): void { $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping; } @@ -1405,10 +1512,8 @@ class Spreadsheet * Set the first sheet in the book view. * * @param int $firstSheetIndex First sheet in book view - * - * @throws Exception if the given value is invalid */ - public function setFirstSheetIndex($firstSheetIndex) + public function setFirstSheetIndex($firstSheetIndex): void { if ($firstSheetIndex >= 0) { $this->firstSheetIndex = (int) $firstSheetIndex; @@ -1444,10 +1549,8 @@ class Spreadsheet * user interface. * * @param string $visibility visibility status of the workbook - * - * @throws Exception if the given value is invalid */ - public function setVisibility($visibility) + public function setVisibility($visibility): void { if ($visibility === null) { $visibility = self::VISIBILITY_VISIBLE; @@ -1476,10 +1579,8 @@ class Spreadsheet * TabRatio is assumed to be out of 1000 of the horizontal window width. * * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar - * - * @throws Exception if the given value is invalid */ - public function setTabRatio($tabRatio) + public function setTabRatio($tabRatio): void { if ($tabRatio >= 0 || $tabRatio <= 1000) { $this->tabRatio = (int) $tabRatio; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php index b4df792b167..04a089fe420 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php @@ -28,6 +28,10 @@ class Alignment extends Supervisor const READORDER_LTR = 1; const READORDER_RTL = 2; + // Special value for Text Rotation + const TEXTROTATION_STACK_EXCEL = 255; + const TEXTROTATION_STACK_PHPSPREADSHEET = -165; // 90 - 255 + /** * Horizontal alignment. * @@ -138,9 +142,7 @@ class Alignment extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Alignment + * @return $this */ public function applyFromArray(array $pStyles) { @@ -193,7 +195,7 @@ class Alignment extends Supervisor * * @param string $pValue see self::HORIZONTAL_* * - * @return Alignment + * @return $this */ public function setHorizontal($pValue) { @@ -230,7 +232,7 @@ class Alignment extends Supervisor * * @param string $pValue see self::VERTICAL_* * - * @return Alignment + * @return $this */ public function setVertical($pValue) { @@ -267,19 +269,17 @@ class Alignment extends Supervisor * * @param int $pValue * - * @throws PhpSpreadsheetException - * - * @return Alignment + * @return $this */ public function setTextRotation($pValue) { // Excel2007 value 255 => PhpSpreadsheet value -165 - if ($pValue == 255) { - $pValue = -165; + if ($pValue == self::TEXTROTATION_STACK_EXCEL) { + $pValue = self::TEXTROTATION_STACK_PHPSPREADSHEET; } // Set rotation - if (($pValue >= -90 && $pValue <= 90) || $pValue == -165) { + if (($pValue >= -90 && $pValue <= 90) || $pValue == self::TEXTROTATION_STACK_PHPSPREADSHEET) { if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['textRotation' => $pValue]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); @@ -312,7 +312,7 @@ class Alignment extends Supervisor * * @param bool $pValue * - * @return Alignment + * @return $this */ public function setWrapText($pValue) { @@ -348,7 +348,7 @@ class Alignment extends Supervisor * * @param bool $pValue * - * @return Alignment + * @return $this */ public function setShrinkToFit($pValue) { @@ -384,14 +384,16 @@ class Alignment extends Supervisor * * @param int $pValue * - * @return Alignment + * @return $this */ public function setIndent($pValue) { if ($pValue > 0) { - if ($this->getHorizontal() != self::HORIZONTAL_GENERAL && + if ( + $this->getHorizontal() != self::HORIZONTAL_GENERAL && $this->getHorizontal() != self::HORIZONTAL_LEFT && - $this->getHorizontal() != self::HORIZONTAL_RIGHT) { + $this->getHorizontal() != self::HORIZONTAL_RIGHT + ) { $pValue = 0; // indent not supported } } @@ -424,7 +426,7 @@ class Alignment extends Supervisor * * @param int $pValue * - * @return Alignment + * @return $this */ public function setReadOrder($pValue) { @@ -463,4 +465,18 @@ class Alignment extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'horizontal', $this->getHorizontal()); + $this->exportArray2($exportedArray, 'indent', $this->getIndent()); + $this->exportArray2($exportedArray, 'readOrder', $this->getReadOrder()); + $this->exportArray2($exportedArray, 'shrinkToFit', $this->getShrinkToFit()); + $this->exportArray2($exportedArray, 'textRotation', $this->getTextRotation()); + $this->exportArray2($exportedArray, 'vertical', $this->getVertical()); + $this->exportArray2($exportedArray, 'wrapText', $this->getWrapText()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php index c957cf595a9..1d3096f01d5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php @@ -69,21 +69,11 @@ class Border extends Supervisor * Get the shared style component for the currently active cell in currently active sheet. * Only used for style supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getSharedComponent() { switch ($this->parentPropertyName) { - case 'allBorders': - case 'horizontal': - case 'inside': - case 'outline': - case 'vertical': - throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.'); - - break; case 'bottom': return $this->parent->getSharedComponent()->getBottom(); case 'diagonal': @@ -95,6 +85,8 @@ class Border extends Supervisor case 'top': return $this->parent->getSharedComponent()->getTop(); } + + throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.'); } /** @@ -125,9 +117,7 @@ class Border extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Border + * @return $this */ public function applyFromArray(array $pStyles) { @@ -166,7 +156,7 @@ class Border extends Supervisor * When passing a boolean, FALSE equates Border::BORDER_NONE * and TRUE to Border::BORDER_MEDIUM * - * @return Border + * @return $this */ public function setBorderStyle($pValue) { @@ -198,11 +188,7 @@ class Border extends Supervisor /** * Set Border Color. * - * @param Color $pValue - * - * @throws PhpSpreadsheetException - * - * @return Border + * @return $this */ public function setColor(Color $pValue) { @@ -236,4 +222,13 @@ class Border extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'borderStyle', $this->getBorderStyle()); + $this->exportArray2($exportedArray, 'color', $this->getColor()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php index a1d6759b936..a1acfdd4b95 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php @@ -195,9 +195,7 @@ class Borders extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Borders + * @return $this */ public function applyFromArray(array $pStyles) { @@ -286,8 +284,6 @@ class Borders extends Supervisor /** * Get AllBorders (pseudo-border). Only applies to supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getAllBorders() @@ -302,8 +298,6 @@ class Borders extends Supervisor /** * Get Outline (pseudo-border). Only applies to supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getOutline() @@ -318,8 +312,6 @@ class Borders extends Supervisor /** * Get Inside (pseudo-border). Only applies to supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getInside() @@ -334,8 +326,6 @@ class Borders extends Supervisor /** * Get Vertical (pseudo-border). Only applies to supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getVertical() @@ -350,8 +340,6 @@ class Borders extends Supervisor /** * Get Horizontal (pseudo-border). Only applies to supervisor. * - * @throws PhpSpreadsheetException - * * @return Border */ public function getHorizontal() @@ -382,7 +370,7 @@ class Borders extends Supervisor * * @param int $pValue see self::DIAGONAL_* * - * @return Borders + * @return $this */ public function setDiagonalDirection($pValue) { @@ -420,4 +408,17 @@ class Borders extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'bottom', $this->getBottom()); + $this->exportArray2($exportedArray, 'diagonal', $this->getDiagonal()); + $this->exportArray2($exportedArray, 'diagonalDirection', $this->getDiagonalDirection()); + $this->exportArray2($exportedArray, 'left', $this->getLeft()); + $this->exportArray2($exportedArray, 'right', $this->getRight()); + $this->exportArray2($exportedArray, 'top', $this->getTop()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php index 60e4a8ce75e..ad598f113a4 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Style; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; - class Color extends Supervisor { const NAMED_COLORS = [ @@ -73,14 +71,14 @@ class Color extends Supervisor */ public function getSharedComponent() { - switch ($this->parentPropertyName) { - case 'endColor': - return $this->parent->getSharedComponent()->getEndColor(); - case 'color': - return $this->parent->getSharedComponent()->getColor(); - case 'startColor': - return $this->parent->getSharedComponent()->getStartColor(); + if ($this->parentPropertyName === 'endColor') { + return $this->parent->getSharedComponent()->getEndColor(); } + if ($this->parentPropertyName === 'startColor') { + return $this->parent->getSharedComponent()->getStartColor(); + } + + return $this->parent->getSharedComponent()->getColor(); } /** @@ -104,9 +102,7 @@ class Color extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Color + * @return $this */ public function applyFromArray(array $pStyles) { @@ -143,7 +139,7 @@ class Color extends Supervisor * * @param string $pValue see self::COLOR_* * - * @return Color + * @return $this */ public function setARGB($pValue) { @@ -179,7 +175,7 @@ class Color extends Supervisor * * @param string $pValue RGB value * - * @return Color + * @return $this */ public function setRGB($pValue) { @@ -266,6 +262,7 @@ class Color extends Supervisor public static function changeBrightness($hex, $adjustPercentage) { $rgba = (strlen($hex) === 8); + $adjustPercentage = max(-1.0, min(1.0, $adjustPercentage)); $red = self::getRed($hex, false); $green = self::getGreen($hex, false); @@ -280,22 +277,6 @@ class Color extends Supervisor $blue += $blue * $adjustPercentage; } - if ($red < 0) { - $red = 0; - } elseif ($red > 255) { - $red = 255; - } - if ($green < 0) { - $green = 0; - } elseif ($green > 255) { - $green = 255; - } - if ($blue < 0) { - $blue = 0; - } elseif ($blue > 255) { - $blue = 255; - } - $rgb = strtoupper( str_pad(dechex((int) $red), 2, '0', 0) . str_pad(dechex((int) $green), 2, '0', 0) . @@ -312,7 +293,7 @@ class Color extends Supervisor * @param bool $background Flag to indicate whether default background or foreground colour * should be returned if the indexed colour doesn't exist * - * @return Color + * @return self */ public static function indexedColor($pIndex, $background = false) { @@ -408,4 +389,12 @@ class Color extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'argb', $this->getARGB()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php index 91a000dba4a..e4fe0acc7f6 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php @@ -12,6 +12,7 @@ class Conditional implements IComparable const CONDITION_CONTAINSTEXT = 'containsText'; const CONDITION_EXPRESSION = 'expression'; const CONDITION_CONTAINSBLANKS = 'containsBlanks'; + const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks'; // Operator types const OPERATOR_NONE = ''; @@ -26,6 +27,7 @@ class Conditional implements IComparable const OPERATOR_CONTAINSTEXT = 'containsText'; const OPERATOR_NOTCONTAINS = 'notContains'; const OPERATOR_BETWEEN = 'between'; + const OPERATOR_NOTBETWEEN = 'notBetween'; /** * Condition type. @@ -93,7 +95,7 @@ class Conditional implements IComparable * * @param string $pValue Condition type, see self::CONDITION_* * - * @return Conditional + * @return $this */ public function setConditionType($pValue) { @@ -117,7 +119,7 @@ class Conditional implements IComparable * * @param string $pValue Conditional operator type, see self::OPERATOR_* * - * @return Conditional + * @return $this */ public function setOperatorType($pValue) { @@ -141,7 +143,7 @@ class Conditional implements IComparable * * @param string $value * - * @return Conditional + * @return $this */ public function setText($value) { @@ -165,7 +167,7 @@ class Conditional implements IComparable * * @param bool $value * - * @return Conditional + * @return $this */ public function setStopIfTrue($value) { @@ -187,9 +189,9 @@ class Conditional implements IComparable /** * Set Conditions. * - * @param string[] $pValue Condition + * @param bool|float|int|string|string[] $pValue Condition * - * @return Conditional + * @return $this */ public function setConditions($pValue) { @@ -206,7 +208,7 @@ class Conditional implements IComparable * * @param string $pValue Condition * - * @return Conditional + * @return $this */ public function addCondition($pValue) { @@ -230,9 +232,9 @@ class Conditional implements IComparable * * @param Style $pValue * - * @return Conditional + * @return $this */ - public function setStyle(Style $pValue = null) + public function setStyle(?Style $pValue = null) { $this->style = $pValue; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php index ce616c9d5d3..3891bc47378 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Style; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; - class Fill extends Supervisor { // Fill types @@ -139,9 +137,7 @@ class Fill extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Fill + * @return $this */ public function applyFromArray(array $pStyles) { @@ -188,7 +184,7 @@ class Fill extends Supervisor * * @param string $pValue Fill type, see self::FILL_* * - * @return Fill + * @return $this */ public function setFillType($pValue) { @@ -221,7 +217,7 @@ class Fill extends Supervisor * * @param float $pValue * - * @return Fill + * @return $this */ public function setRotation($pValue) { @@ -248,11 +244,7 @@ class Fill extends Supervisor /** * Set Start Color. * - * @param Color $pValue - * - * @throws PhpSpreadsheetException - * - * @return Fill + * @return $this */ public function setStartColor(Color $pValue) { @@ -282,11 +274,7 @@ class Fill extends Supervisor /** * Set End Color. * - * @param Color $pValue - * - * @throws PhpSpreadsheetException - * - * @return Fill + * @return $this */ public function setEndColor(Color $pValue) { @@ -323,4 +311,15 @@ class Fill extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'endColor', $this->getEndColor()); + $this->exportArray2($exportedArray, 'fillType', $this->getFillType()); + $this->exportArray2($exportedArray, 'rotation', $this->getRotation()); + $this->exportArray2($exportedArray, 'startColor', $this->getStartColor()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php index 6d8e23b1954..ad405708373 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Style; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; - class Font extends Supervisor { // Underline types @@ -159,9 +157,7 @@ class Font extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Font + * @return $this */ public function applyFromArray(array $pStyles) { @@ -219,7 +215,7 @@ class Font extends Supervisor * * @param string $pValue * - * @return Font + * @return $this */ public function setName($pValue) { @@ -255,7 +251,7 @@ class Font extends Supervisor * * @param float $pValue * - * @return Font + * @return $this */ public function setSize($pValue) { @@ -291,7 +287,7 @@ class Font extends Supervisor * * @param bool $pValue * - * @return Font + * @return $this */ public function setBold($pValue) { @@ -327,7 +323,7 @@ class Font extends Supervisor * * @param bool $pValue * - * @return Font + * @return $this */ public function setItalic($pValue) { @@ -361,21 +357,18 @@ class Font extends Supervisor /** * Set Superscript. * - * @param bool $pValue - * - * @return Font + * @return $this */ - public function setSuperscript($pValue) + public function setSuperscript(bool $pValue) { - if ($pValue == '') { - $pValue = false; - } if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['superscript' => $pValue]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); } else { $this->superscript = $pValue; - $this->subscript = !$pValue; + if ($this->superscript) { + $this->subscript = false; + } } return $this; @@ -398,21 +391,18 @@ class Font extends Supervisor /** * Set Subscript. * - * @param bool $pValue - * - * @return Font + * @return $this */ - public function setSubscript($pValue) + public function setSubscript(bool $pValue) { - if ($pValue == '') { - $pValue = false; - } if ($this->isSupervisor) { $styleArray = $this->getStyleArray(['subscript' => $pValue]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); } else { $this->subscript = $pValue; - $this->superscript = !$pValue; + if ($this->subscript) { + $this->superscript = false; + } } return $this; @@ -439,7 +429,7 @@ class Font extends Supervisor * If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE, * false equates to UNDERLINE_NONE * - * @return Font + * @return $this */ public function setUnderline($pValue) { @@ -477,7 +467,7 @@ class Font extends Supervisor * * @param bool $pValue * - * @return Font + * @return $this */ public function setStrikethrough($pValue) { @@ -508,11 +498,7 @@ class Font extends Supervisor /** * Set Color. * - * @param Color $pValue - * - * @throws PhpSpreadsheetException - * - * @return Font + * @return $this */ public function setColor(Color $pValue) { @@ -553,4 +539,20 @@ class Font extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'bold', $this->getBold()); + $this->exportArray2($exportedArray, 'color', $this->getColor()); + $this->exportArray2($exportedArray, 'italic', $this->getItalic()); + $this->exportArray2($exportedArray, 'name', $this->getName()); + $this->exportArray2($exportedArray, 'size', $this->getSize()); + $this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough()); + $this->exportArray2($exportedArray, 'subscript', $this->getSubscript()); + $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript()); + $this->exportArray2($exportedArray, 'underline', $this->getUnderline()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php index 7efa759e965..0b761bd3c3e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php @@ -3,7 +3,6 @@ namespace PhpOffice\PhpSpreadsheet\Style; use PhpOffice\PhpSpreadsheet\Calculation\MathTrig; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; @@ -137,9 +136,7 @@ class NumberFormat extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return NumberFormat + * @return $this */ public function applyFromArray(array $pStyles) { @@ -176,7 +173,7 @@ class NumberFormat extends Supervisor * * @param string $pValue see self::FORMAT_* * - * @return NumberFormat + * @return $this */ public function setFormatCode($pValue) { @@ -213,7 +210,7 @@ class NumberFormat extends Supervisor * * @param int $pValue * - * @return NumberFormat + * @return $this */ public function setBuiltInFormatCode($pValue) { @@ -231,7 +228,7 @@ class NumberFormat extends Supervisor /** * Fill built-in format codes. */ - private static function fillBuiltInFormatCodes() + private static function fillBuiltInFormatCodes(): void { // [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance] // 18.8.30. numFmt (Number Format) @@ -367,7 +364,7 @@ class NumberFormat extends Supervisor self::fillBuiltInFormatCodes(); // Lookup format code - if (isset(self::$flippedBuiltInFormats[$formatCode])) { + if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) { return self::$flippedBuiltInFormats[$formatCode]; } @@ -398,43 +395,43 @@ class NumberFormat extends Supervisor * @var array */ private static $dateFormatReplacements = [ - // first remove escapes related to non-format characters - '\\' => '', - // 12-hour suffix - 'am/pm' => 'A', - // 4-digit year - 'e' => 'Y', - 'yyyy' => 'Y', - // 2-digit year - 'yy' => 'y', - // first letter of month - no php equivalent - 'mmmmm' => 'M', - // full month name - 'mmmm' => 'F', - // short month name - 'mmm' => 'M', - // mm is minutes if time, but can also be month w/leading zero - // so we try to identify times be the inclusion of a : separator in the mask - // It isn't perfect, but the best way I know how - ':mm' => ':i', - 'mm:' => 'i:', - // month leading zero - 'mm' => 'm', - // month no leading zero - 'm' => 'n', - // full day of week name - 'dddd' => 'l', - // short day of week name - 'ddd' => 'D', - // days leading zero - 'dd' => 'd', - // days no leading zero - 'd' => 'j', - // seconds - 'ss' => 's', - // fractional seconds - no php equivalent - '.s' => '', - ]; + // first remove escapes related to non-format characters + '\\' => '', + // 12-hour suffix + 'am/pm' => 'A', + // 4-digit year + 'e' => 'Y', + 'yyyy' => 'Y', + // 2-digit year + 'yy' => 'y', + // first letter of month - no php equivalent + 'mmmmm' => 'M', + // full month name + 'mmmm' => 'F', + // short month name + 'mmm' => 'M', + // mm is minutes if time, but can also be month w/leading zero + // so we try to identify times be the inclusion of a : separator in the mask + // It isn't perfect, but the best way I know how + ':mm' => ':i', + 'mm:' => 'i:', + // month leading zero + 'mm' => 'm', + // month no leading zero + 'm' => 'n', + // full day of week name + 'dddd' => 'l', + // short day of week name + 'ddd' => 'D', + // days leading zero + 'dd' => 'd', + // days no leading zero + 'd' => 'j', + // seconds + 'ss' => 's', + // fractional seconds - no php equivalent + '.s' => '', + ]; /** * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock). @@ -466,7 +463,7 @@ class NumberFormat extends Supervisor return '\\' . implode('\\', str_split($matches[1])); } - private static function formatAsDate(&$value, &$format) + private static function formatAsDate(&$value, &$format): void { // strip off first part containing e.g. [$-F800] or [$USD-409] // general syntax: [$-] @@ -508,7 +505,7 @@ class NumberFormat extends Supervisor $value = $dateObj->format($format); } - private static function formatAsPercentage(&$value, &$format) + private static function formatAsPercentage(&$value, &$format): void { if ($format === self::FORMAT_PERCENTAGE) { $value = round((100 * $value), 0) . '%'; @@ -526,21 +523,29 @@ class NumberFormat extends Supervisor } } - private static function formatAsFraction(&$value, &$format) + private static function formatAsFraction(&$value, &$format): void { $sign = ($value < 0) ? '-' : ''; $integerPart = floor(abs($value)); $decimalPart = trim(fmod(abs($value), 1), '0.'); $decimalLength = strlen($decimalPart); - $decimalDivisor = pow(10, $decimalLength); + $decimalDivisor = 10 ** $decimalLength; $GCD = MathTrig::GCD($decimalPart, $decimalDivisor); $adjustedDecimalPart = $decimalPart / $GCD; $adjustedDecimalDivisor = $decimalDivisor / $GCD; - if ((strpos($format, '0') !== false) || (strpos($format, '#') !== false) || (substr($format, 0, 3) == '? ?')) { + if ((strpos($format, '0') !== false)) { + $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor"; + } elseif ((strpos($format, '#') !== false)) { + if ($integerPart == 0) { + $value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor"; + } else { + $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor"; + } + } elseif ((substr($format, 0, 3) == '? ?')) { if ($integerPart == 0) { $integerPart = ''; } @@ -656,9 +661,12 @@ class NumberFormat extends Supervisor private static function formatAsNumber($value, $format) { - if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) { - return 'EUR ' . sprintf('%1.2f', $value); - } + // The "_" in this string has already been stripped out, + // so this test is never true. Furthermore, testing + // on Excel shows this format uses Euro symbol, not "EUR". + //if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) { + // return 'EUR ' . sprintf('%1.2f', $value); + //} // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols $format = str_replace(['"', '*'], '', $format); @@ -678,7 +686,7 @@ class NumberFormat extends Supervisor $scale = 1; // same as no scale $matches = []; if (preg_match('/(#|0)(,+)/', $format, $matches)) { - $scale = pow(1000, strlen($matches[2])); + $scale = 1000 ** strlen($matches[2]); // strip the commas $format = preg_replace('/0,+/', '0', $format); @@ -720,6 +728,89 @@ class NumberFormat extends Supervisor return $value; } + private static function splitFormatCompare($value, $cond, $val, $dfcond, $dfval) + { + if (!$cond) { + $cond = $dfcond; + $val = $dfval; + } + switch ($cond) { + case '>': + return $value > $val; + + case '<': + return $value < $val; + + case '<=': + return $value <= $val; + + case '<>': + return $value != $val; + + case '=': + return $value == $val; + } + + return $value >= $val; + } + + private static function splitFormat($sections, $value) + { + // Extract the relevant section depending on whether number is positive, negative, or zero? + // Text not supported yet. + // Here is how the sections apply to various values in Excel: + // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT] + // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE] + // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO] + // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT] + $cnt = count($sections); + $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . ')\\]/'; + $cond_regex = '/\\[(>|>=|<|<=|=|<>)([+-]?\\d+([.]\\d+)?)\\]/'; + $colors = ['', '', '', '', '']; + $condops = ['', '', '', '', '']; + $condvals = [0, 0, 0, 0, 0]; + for ($idx = 0; $idx < $cnt; ++$idx) { + if (preg_match($color_regex, $sections[$idx], $matches)) { + $colors[$idx] = $matches[0]; + $sections[$idx] = preg_replace($color_regex, '', $sections[$idx]); + } + if (preg_match($cond_regex, $sections[$idx], $matches)) { + $condops[$idx] = $matches[1]; + $condvals[$idx] = $matches[2]; + $sections[$idx] = preg_replace($cond_regex, '', $sections[$idx]); + } + } + $color = $colors[0]; + $format = $sections[0]; + $absval = $value; + switch ($cnt) { + case 2: + $absval = abs($value); + if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>=', 0)) { + $color = $colors[1]; + $format = $sections[1]; + } + + break; + case 3: + case 4: + $absval = abs($value); + if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>', 0)) { + if (self::splitFormatCompare($value, $condops[1], $condvals[1], '<', 0)) { + $color = $colors[1]; + $format = $sections[1]; + } else { + $color = $colors[2]; + $format = $sections[2]; + } + } + + break; + } + + return [$color, $format, $absval]; + } + /** * Convert a value in a pre-defined format to a PHP string. * @@ -748,50 +839,12 @@ class NumberFormat extends Supervisor // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal) $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format); - // Extract the relevant section depending on whether number is positive, negative, or zero? - // Text not supported yet. - // Here is how the sections apply to various values in Excel: - // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT] - // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE] - // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO] - // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT] - switch (count($sections)) { - case 1: - $format = $sections[0]; - - break; - case 2: - $format = ($value >= 0) ? $sections[0] : $sections[1]; - $value = abs($value); // Use the absolute value - break; - case 3: - $format = ($value > 0) ? - $sections[0] : (($value < 0) ? - $sections[1] : $sections[2]); - $value = abs($value); // Use the absolute value - break; - case 4: - $format = ($value > 0) ? - $sections[0] : (($value < 0) ? - $sections[1] : $sections[2]); - $value = abs($value); // Use the absolute value - break; - default: - // something is wrong, just use first section - $format = $sections[0]; - - break; - } + [$colors, $format, $value] = self::splitFormat($sections, $value); // In Excel formats, "_" is used to add spacing, // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space $format = preg_replace('/_./', ' ', $format); - // Save format with color information for later use below - $formatColor = $format; - // Strip colour information - $color_regex = '/\[(' . implode('|', Color::NAMED_COLORS) . ')\]/'; - $format = preg_replace($color_regex, '', $format); // Let's begin inspecting the format and converting the value to a formatted string // Check for date/time characters (not inside quotes) @@ -812,9 +865,17 @@ class NumberFormat extends Supervisor // Additional formatting provided by callback function if ($callBack !== null) { [$writerInstance, $function] = $callBack; - $value = $writerInstance->$function($value, $formatColor); + $value = $writerInstance->$function($value, $colors); } return $value; } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'formatCode', $this->getFormatCode()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php index b5feb53486e..9ae7d4bb361 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Style; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; - class Protection extends Supervisor { /** Protection styles */ @@ -84,9 +82,7 @@ class Protection extends Supervisor * * @param array $pStyles Array containing style information * - * @throws PhpSpreadsheetException - * - * @return Protection + * @return $this */ public function applyFromArray(array $pStyles) { @@ -123,7 +119,7 @@ class Protection extends Supervisor * * @param string $pValue see self::PROTECTION_* * - * @return Protection + * @return $this */ public function setLocked($pValue) { @@ -156,7 +152,7 @@ class Protection extends Supervisor * * @param string $pValue see self::PROTECTION_* * - * @return Protection + * @return $this */ public function setHidden($pValue) { @@ -187,4 +183,13 @@ class Protection extends Supervisor __CLASS__ ); } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'locked', $this->getLocked()); + $this->exportArray2($exportedArray, 'hidden', $this->getHidden()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php index 9cdfc1b1bce..f7c1be23fc4 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php @@ -42,13 +42,6 @@ class Style extends Supervisor */ protected $numberFormat; - /** - * Conditional styles. - * - * @var Conditional[] - */ - protected $conditionalStyles; - /** * Protection. * @@ -85,7 +78,6 @@ class Style extends Supervisor parent::__construct($isSupervisor); // Initialise values - $this->conditionalStyles = []; $this->font = new Font($isSupervisor, $isConditional); $this->fill = new Fill($isSupervisor, $isConditional); $this->borders = new Borders($isSupervisor, $isConditional); @@ -189,7 +181,7 @@ class Style extends Supervisor * @param array $pStyles Array containing style information * @param bool $pAdvanced advanced mode for setting borders * - * @return Style + * @return $this */ public function applyFromArray(array $pStyles, $pAdvanced = true) { @@ -212,6 +204,8 @@ class Style extends Supervisor $rangeEnd = Coordinate::coordinateFromString($rangeB); // Translate column into index + $rangeStart0 = $rangeStart[0]; + $rangeEnd0 = $rangeEnd[0]; $rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]); $rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]); @@ -361,6 +355,13 @@ class Style extends Supervisor for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true; } + foreach ($this->getActiveSheet()->getColumnIterator($rangeStart0, $rangeEnd0) as $columnIterator) { + $cellIterator = $columnIterator->getCellIterator(); + $cellIterator->setIterateOnlyExistingCells(true); + foreach ($cellIterator as $columnCell) { + $columnCell->getStyle()->applyFromArray($pStyles); + } + } break; case 'ROW': @@ -372,6 +373,13 @@ class Style extends Supervisor $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true; } } + foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) { + $cellIterator = $rowIterator->getCellIterator(); + $cellIterator->setIterateOnlyExistingCells(true); + foreach ($cellIterator as $rowCell) { + $rowCell->getStyle()->applyFromArray($pStyles); + } + } break; case 'CELL': @@ -483,9 +491,7 @@ class Style extends Supervisor /** * Set font. * - * @param Font $font - * - * @return Style + * @return $this */ public function setFont(Font $font) { @@ -539,7 +545,7 @@ class Style extends Supervisor * * @param Conditional[] $pValue Array of conditional styles * - * @return Style + * @return $this */ public function setConditionalStyles(array $pValue) { @@ -577,7 +583,7 @@ class Style extends Supervisor * * @param bool $pValue * - * @return Style + * @return $this */ public function setQuotePrefix($pValue) { @@ -601,18 +607,12 @@ class Style extends Supervisor */ public function getHashCode() { - $hashConditionals = ''; - foreach ($this->conditionalStyles as $conditional) { - $hashConditionals .= $conditional->getHashCode(); - } - return md5( $this->fill->getHashCode() . $this->font->getHashCode() . $this->borders->getHashCode() . $this->alignment->getHashCode() . $this->numberFormat->getHashCode() . - $hashConditionals . $this->protection->getHashCode() . ($this->quotePrefix ? 't' : 'f') . __CLASS__ @@ -634,8 +634,22 @@ class Style extends Supervisor * * @param int $pValue */ - public function setIndex($pValue) + public function setIndex($pValue): void { $this->index = $pValue; } + + protected function exportArray1(): array + { + $exportedArray = []; + $this->exportArray2($exportedArray, 'alignment', $this->getAlignment()); + $this->exportArray2($exportedArray, 'borders', $this->getBorders()); + $this->exportArray2($exportedArray, 'fill', $this->getFill()); + $this->exportArray2($exportedArray, 'font', $this->getFont()); + $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat()); + $this->exportArray2($exportedArray, 'protection', $this->getProtection()); + $this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix()); + + return $exportedArray; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php index 2d1a27266b1..7f655bef72a 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php @@ -48,7 +48,7 @@ abstract class Supervisor implements IComparable * @param Spreadsheet|Style $parent * @param null|string $parentPropertyName * - * @return Supervisor + * @return $this */ public function bindParent($parent, $parentPropertyName = null) { @@ -114,4 +114,45 @@ abstract class Supervisor implements IComparable } } } + + /** + * Export style as array. + * + * Available to anything which extends this class: + * Alignment, Border, Borders, Color, Fill, Font, + * NumberFormat, Protection, and Style. + */ + final public function exportArray(): array + { + return $this->exportArray1(); + } + + /** + * Abstract method to be implemented in anything which + * extends this class. + * + * This method invokes exportArray2 with the names and values + * of all properties to be included in output array, + * returning that array to exportArray, then to caller. + */ + abstract protected function exportArray1(): array; + + /** + * Populate array from exportArray1. + * This method is available to anything which extends this class. + * The parameter index is the key to be added to the array. + * The parameter objOrValue is either a primitive type, + * which is the value added to the array, + * or a Style object to be recursively added via exportArray. + * + * @param mixed $objOrValue + */ + final protected function exportArray2(array &$exportedArray, string $index, $objOrValue): void + { + if ($objOrValue instanceof self) { + $exportedArray[$index] = $objOrValue->exportArray(); + } else { + $exportedArray[$index] = $objOrValue; + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php index b44492546ce..c2ded195c78 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -38,7 +38,7 @@ class AutoFilter * @param string $pRange Cell range (i.e. A1:E10) * @param Worksheet $pSheet */ - public function __construct($pRange = '', Worksheet $pSheet = null) + public function __construct($pRange = '', ?Worksheet $pSheet = null) { $this->range = $pRange; $this->workSheet = $pSheet; @@ -59,9 +59,9 @@ class AutoFilter * * @param Worksheet $pSheet * - * @return AutoFilter + * @return $this */ - public function setParent(Worksheet $pSheet = null) + public function setParent(?Worksheet $pSheet = null) { $this->workSheet = $pSheet; @@ -83,9 +83,7 @@ class AutoFilter * * @param string $pRange Cell range (i.e. A1:E10) * - * @throws PhpSpreadsheetException - * - * @return AutoFilter + * @return $this */ public function setRange($pRange) { @@ -132,8 +130,6 @@ class AutoFilter * * @param string $column Column name (e.g. A) * - * @throws PhpSpreadsheetException - * * @return int The column offset within the autofilter range */ public function testColumnInRange($column) @@ -156,8 +152,6 @@ class AutoFilter * * @param string $pColumn Column name (e.g. A) * - * @throws PhpSpreadsheetException - * * @return int The offset of the specified column within the autofilter range */ public function getColumnOffset($pColumn) @@ -170,8 +164,6 @@ class AutoFilter * * @param string $pColumn Column name (e.g. A) * - * @throws PhpSpreadsheetException - * * @return AutoFilter\Column */ public function getColumn($pColumn) @@ -190,8 +182,6 @@ class AutoFilter * * @param int $pColumnOffset Column offset within range (starting from 0) * - * @throws PhpSpreadsheetException - * * @return AutoFilter\Column */ public function getColumnByOffset($pColumnOffset) @@ -208,9 +198,7 @@ class AutoFilter * @param AutoFilter\Column|string $pColumn * A simple string containing a Column ID like 'A' is permitted * - * @throws PhpSpreadsheetException - * - * @return AutoFilter + * @return $this */ public function setColumn($pColumn) { @@ -239,9 +227,7 @@ class AutoFilter * * @param string $pColumn Column name (e.g. A) * - * @throws PhpSpreadsheetException - * - * @return AutoFilter + * @return $this */ public function clearColumn($pColumn) { @@ -264,7 +250,7 @@ class AutoFilter * @param string $fromColumn Column name (e.g. A) * @param string $toColumn Column name (e.g. B) * - * @return AutoFilter + * @return $this */ public function shiftColumn($fromColumn, $toColumn) { @@ -615,9 +601,7 @@ class AutoFilter /** * Apply the AutoFilter rules to the AutoFilter Range. * - * @throws PhpSpreadsheetException - * - * @return AutoFilter + * @return $this */ public function showHideRows() { @@ -659,28 +643,40 @@ class AutoFilter ]; foreach ($ruleDataSet as $ruleValue) { $date = $time = ''; - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '') + ) { $date .= sprintf('%04d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]); } - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '') + ) { $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]); } - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '') + ) { $date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]); } - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '') + ) { $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]); } - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '') + ) { $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]); } - if ((isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) && - ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')) { + if ( + (isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) && + ($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '') + ) { $time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]); } $dateTime = $date . $time; @@ -728,8 +724,10 @@ class AutoFilter foreach ($rules as $rule) { // We should only ever have one Dynamic Filter Rule anyway $dynamicRuleType = $rule->getGrouping(); - if (($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) || - ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)) { + if ( + ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) || + ($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE) + ) { // Number (Average) based // Calculate the average $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')'; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php index 25ccffda8e0..09584a7aaac 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php @@ -94,7 +94,7 @@ class Column * @param string $pColumn Column (e.g. A) * @param AutoFilter $pParent Autofilter for this column */ - public function __construct($pColumn, AutoFilter $pParent = null) + public function __construct($pColumn, ?AutoFilter $pParent = null) { $this->columnIndex = $pColumn; $this->parent = $pParent; @@ -115,9 +115,7 @@ class Column * * @param string $pColumn Column (e.g. A) * - * @throws PhpSpreadsheetException - * - * @return Column + * @return $this */ public function setColumnIndex($pColumn) { @@ -147,9 +145,9 @@ class Column * * @param AutoFilter $pParent * - * @return Column + * @return $this */ - public function setParent(AutoFilter $pParent = null) + public function setParent(?AutoFilter $pParent = null) { $this->parent = $pParent; @@ -171,9 +169,7 @@ class Column * * @param string $pFilterType * - * @throws PhpSpreadsheetException - * - * @return Column + * @return $this */ public function setFilterType($pFilterType) { @@ -201,9 +197,7 @@ class Column * * @param string $pJoin And/Or * - * @throws PhpSpreadsheetException - * - * @return Column + * @return $this */ public function setJoin($pJoin) { @@ -223,7 +217,7 @@ class Column * * @param string[] $attributes * - * @return Column + * @return $this */ public function setAttributes(array $attributes) { @@ -238,7 +232,7 @@ class Column * @param string $pName Attribute Name * @param string $pValue Attribute Value * - * @return Column + * @return $this */ public function setAttribute($pName, $pValue) { @@ -314,9 +308,7 @@ class Column /** * Add a new AutoFilter Column Rule to the ruleset. * - * @param Column\Rule $pRule - * - * @return Column + * @return $this */ public function addRule(Column\Rule $pRule) { @@ -332,7 +324,7 @@ class Column * * @param int $pIndex Rule index in the ruleset array * - * @return Column + * @return $this */ public function deleteRule($pIndex) { @@ -350,7 +342,7 @@ class Column /** * Delete all AutoFilter Column Rules. * - * @return Column + * @return $this */ public function clearRules() { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php index 450bccdb6ac..1aacb0cb8fe 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php @@ -240,7 +240,7 @@ class Rule * * @param Column $pParent */ - public function __construct(Column $pParent = null) + public function __construct(?Column $pParent = null) { $this->parent = $pParent; } @@ -260,9 +260,7 @@ class Rule * * @param string $pRuleType see self::AUTOFILTER_RULETYPE_* * - * @throws PhpSpreadsheetException - * - * @return Rule + * @return $this */ public function setRuleType($pRuleType) { @@ -290,9 +288,7 @@ class Rule * * @param string|string[] $pValue * - * @throws PhpSpreadsheetException - * - * @return Rule + * @return $this */ public function setValue($pValue) { @@ -334,17 +330,17 @@ class Rule * * @param string $pOperator see self::AUTOFILTER_COLUMN_RULE_* * - * @throws PhpSpreadsheetException - * - * @return Rule + * @return $this */ public function setOperator($pOperator) { if (empty($pOperator)) { $pOperator = self::AUTOFILTER_COLUMN_RULE_EQUAL; } - if ((!in_array($pOperator, self::$operators)) && - (!in_array($pOperator, self::$topTenValue))) { + if ( + (!in_array($pOperator, self::$operators)) && + (!in_array($pOperator, self::$topTenValue)) + ) { throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.'); } $this->operator = $pOperator; @@ -367,16 +363,16 @@ class Rule * * @param string $pGrouping * - * @throws PhpSpreadsheetException - * - * @return Rule + * @return $this */ public function setGrouping($pGrouping) { - if (($pGrouping !== null) && + if ( + ($pGrouping !== null) && (!in_array($pGrouping, self::$dateTimeGroups)) && (!in_array($pGrouping, self::$dynamicTypes)) && - (!in_array($pGrouping, self::$topTenType))) { + (!in_array($pGrouping, self::$topTenType)) + ) { throw new PhpSpreadsheetException('Invalid rule type for column AutoFilter Rule.'); } $this->grouping = $pGrouping; @@ -391,9 +387,7 @@ class Rule * @param string|string[] $pValue * @param string $pGrouping * - * @throws PhpSpreadsheetException - * - * @return Rule + * @return $this */ public function setRule($pOperator, $pValue, $pGrouping = null) { @@ -424,9 +418,9 @@ class Rule * * @param Column $pParent * - * @return Rule + * @return $this */ - public function setParent(Column $pParent = null) + public function setParent(?Column $pParent = null) { $this->parent = $pParent; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php index 98b689729e1..be2f23df7d2 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php @@ -154,7 +154,7 @@ class BaseDrawing implements IComparable * * @param string $pValue * - * @return BaseDrawing + * @return $this */ public function setName($pValue) { @@ -178,7 +178,7 @@ class BaseDrawing implements IComparable * * @param string $description * - * @return BaseDrawing + * @return $this */ public function setDescription($description) { @@ -203,11 +203,9 @@ class BaseDrawing implements IComparable * @param Worksheet $pValue * @param bool $pOverrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet? * - * @throws PhpSpreadsheetException - * - * @return BaseDrawing + * @return $this */ - public function setWorksheet(Worksheet $pValue = null, $pOverrideOld = false) + public function setWorksheet(?Worksheet $pValue = null, $pOverrideOld = false) { if ($this->worksheet === null) { // Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet @@ -220,7 +218,7 @@ class BaseDrawing implements IComparable $iterator = $this->worksheet->getDrawingCollection()->getIterator(); while ($iterator->valid()) { - if ($iterator->current()->getHashCode() == $this->getHashCode()) { + if ($iterator->current()->getHashCode() === $this->getHashCode()) { $this->worksheet->getDrawingCollection()->offsetUnset($iterator->key()); $this->worksheet = null; @@ -253,7 +251,7 @@ class BaseDrawing implements IComparable * * @param string $pValue eg: 'A1' * - * @return BaseDrawing + * @return $this */ public function setCoordinates($pValue) { @@ -277,7 +275,7 @@ class BaseDrawing implements IComparable * * @param int $pValue * - * @return BaseDrawing + * @return $this */ public function setOffsetX($pValue) { @@ -301,7 +299,7 @@ class BaseDrawing implements IComparable * * @param int $pValue * - * @return BaseDrawing + * @return $this */ public function setOffsetY($pValue) { @@ -325,7 +323,7 @@ class BaseDrawing implements IComparable * * @param int $pValue * - * @return BaseDrawing + * @return $this */ public function setWidth($pValue) { @@ -356,7 +354,7 @@ class BaseDrawing implements IComparable * * @param int $pValue * - * @return BaseDrawing + * @return $this */ public function setHeight($pValue) { @@ -386,7 +384,7 @@ class BaseDrawing implements IComparable * @param int $width * @param int $height * - * @return BaseDrawing + * @return $this */ public function setWidthAndHeight($width, $height) { @@ -423,7 +421,7 @@ class BaseDrawing implements IComparable * * @param bool $pValue * - * @return BaseDrawing + * @return $this */ public function setResizeProportional($pValue) { @@ -447,7 +445,7 @@ class BaseDrawing implements IComparable * * @param int $pValue * - * @return BaseDrawing + * @return $this */ public function setRotation($pValue) { @@ -471,9 +469,9 @@ class BaseDrawing implements IComparable * * @param Drawing\Shadow $pValue * - * @return BaseDrawing + * @return $this */ - public function setShadow(Drawing\Shadow $pValue = null) + public function setShadow(?Drawing\Shadow $pValue = null) { $this->shadow = $pValue; @@ -519,10 +517,7 @@ class BaseDrawing implements IComparable } } - /** - * @param null|Hyperlink $pHyperlink - */ - public function setHyperlink(Hyperlink $pHyperlink = null) + public function setHyperlink(?Hyperlink $pHyperlink = null): void { $this->hyperlink = $pHyperlink; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php index d97e33f7d10..45f76cab371 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php @@ -2,9 +2,9 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; +use Iterator; -abstract class CellIterator implements \Iterator +abstract class CellIterator implements Iterator { /** * Worksheet to iterate. @@ -25,7 +25,7 @@ abstract class CellIterator implements \Iterator */ public function __destruct() { - unset($this->worksheet); + $this->worksheet = null; } /** @@ -40,8 +40,6 @@ abstract class CellIterator implements \Iterator /** * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary. - * - * @throws PhpSpreadsheetException */ abstract protected function adjustForExistingOnlyRange(); @@ -49,10 +47,8 @@ abstract class CellIterator implements \Iterator * Set the iterator to loop only existing cells. * * @param bool $value - * - * @throws PhpSpreadsheetException */ - public function setIterateOnlyExistingCells($value) + public function setIterateOnlyExistingCells($value): void { $this->onlyExistingCells = (bool) $value; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php index 098967f726d..410e80735a2 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php @@ -24,7 +24,7 @@ class Column * @param Worksheet $parent * @param string $columnIndex */ - public function __construct(Worksheet $parent = null, $columnIndex = 'A') + public function __construct(?Worksheet $parent = null, $columnIndex = 'A') { // Set parent and column index $this->parent = $parent; @@ -36,7 +36,7 @@ class Column */ public function __destruct() { - unset($this->parent); + $this->parent = null; } /** diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php index 7e8f040d26a..714ee7ce3bf 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php @@ -43,7 +43,7 @@ class ColumnCellIterator extends CellIterator * @param int $startRow The row number at which to start iterating * @param int $endRow Optionally, the row number at which to stop iterating */ - public function __construct(Worksheet $subject = null, $columnIndex = 'A', $startRow = 1, $endRow = null) + public function __construct(?Worksheet $subject = null, $columnIndex = 'A', $startRow = 1, $endRow = null) { // Set subject $this->worksheet = $subject; @@ -57,9 +57,7 @@ class ColumnCellIterator extends CellIterator * * @param int $startRow The row number at which to start iterating * - * @throws PhpSpreadsheetException - * - * @return ColumnCellIterator + * @return $this */ public function resetStart($startRow = 1) { @@ -75,9 +73,7 @@ class ColumnCellIterator extends CellIterator * * @param int $endRow The row number at which to stop iterating * - * @throws PhpSpreadsheetException - * - * @return ColumnCellIterator + * @return $this */ public function resetEnd($endRow = null) { @@ -92,16 +88,15 @@ class ColumnCellIterator extends CellIterator * * @param int $row The row number to set the current pointer at * - * @throws PhpSpreadsheetException - * - * @return ColumnCellIterator + * @return $this */ public function seek($row = 1) { + if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) { + throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist'); + } if (($row < $this->startRow) || ($row > $this->endRow)) { throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})"); - } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) { - throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist'); } $this->currentRow = $row; @@ -111,7 +106,7 @@ class ColumnCellIterator extends CellIterator /** * Rewind the iterator to the starting row. */ - public function rewind() + public function rewind(): void { $this->currentRow = $this->startRow; } @@ -119,7 +114,7 @@ class ColumnCellIterator extends CellIterator /** * Return the current cell in this worksheet column. * - * @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell + * @return \PhpOffice\PhpSpreadsheet\Cell\Cell */ public function current() { @@ -139,25 +134,29 @@ class ColumnCellIterator extends CellIterator /** * Set the iterator to its next value. */ - public function next() + public function next(): void { do { ++$this->currentRow; - } while (($this->onlyExistingCells) && + } while ( + ($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) && - ($this->currentRow <= $this->endRow)); + ($this->currentRow <= $this->endRow) + ); } /** * Set the iterator to its previous value. */ - public function prev() + public function prev(): void { do { --$this->currentRow; - } while (($this->onlyExistingCells) && + } while ( + ($this->onlyExistingCells) && (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->currentRow)) && - ($this->currentRow >= $this->startRow)); + ($this->currentRow >= $this->startRow) + ); } /** @@ -172,26 +171,22 @@ class ColumnCellIterator extends CellIterator /** * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary. - * - * @throws PhpSpreadsheetException */ - protected function adjustForExistingOnlyRange() + protected function adjustForExistingOnlyRange(): void { if ($this->onlyExistingCells) { - while ((!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) && - ($this->startRow <= $this->endRow)) { + while ( + (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) && + ($this->startRow <= $this->endRow) + ) { ++$this->startRow; } - if ($this->startRow > $this->endRow) { - throw new PhpSpreadsheetException('No cells exist within the specified range'); - } - while ((!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) && - ($this->endRow >= $this->startRow)) { + while ( + (!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) && + ($this->endRow >= $this->startRow) + ) { --$this->endRow; } - if ($this->endRow < $this->startRow) { - throw new PhpSpreadsheetException('No cells exist within the specified range'); - } } } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php index e9586a6081c..4e87a344c80 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php @@ -56,7 +56,7 @@ class ColumnDimension extends Dimension * * @param string $pValue * - * @return ColumnDimension + * @return $this */ public function setColumnIndex($pValue) { @@ -80,7 +80,7 @@ class ColumnDimension extends Dimension * * @param float $pValue * - * @return ColumnDimension + * @return $this */ public function setWidth($pValue) { @@ -104,7 +104,7 @@ class ColumnDimension extends Dimension * * @param bool $pValue * - * @return ColumnDimension + * @return $this */ public function setAutoSize($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php index d2b57aad2f5..d0bb20cc968 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php @@ -2,11 +2,12 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet; +use Iterator; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; -class ColumnIterator implements \Iterator +class ColumnIterator implements Iterator { /** * Worksheet to iterate. @@ -56,7 +57,7 @@ class ColumnIterator implements \Iterator */ public function __destruct() { - unset($this->worksheet); + $this->worksheet = null; } /** @@ -64,9 +65,7 @@ class ColumnIterator implements \Iterator * * @param string $startColumn The column address at which to start iterating * - * @throws Exception - * - * @return ColumnIterator + * @return $this */ public function resetStart($startColumn = 'A') { @@ -89,7 +88,7 @@ class ColumnIterator implements \Iterator * * @param string $endColumn The column address at which to stop iterating * - * @return ColumnIterator + * @return $this */ public function resetEnd($endColumn = null) { @@ -104,9 +103,7 @@ class ColumnIterator implements \Iterator * * @param string $column The column address to set the current pointer at * - * @throws PhpSpreadsheetException - * - * @return ColumnIterator + * @return $this */ public function seek($column = 'A') { @@ -122,7 +119,7 @@ class ColumnIterator implements \Iterator /** * Rewind the iterator to the starting column. */ - public function rewind() + public function rewind(): void { $this->currentColumnIndex = $this->startColumnIndex; } @@ -150,7 +147,7 @@ class ColumnIterator implements \Iterator /** * Set the iterator to its next value. */ - public function next() + public function next(): void { ++$this->currentColumnIndex; } @@ -158,7 +155,7 @@ class ColumnIterator implements \Iterator /** * Set the iterator to its previous value. */ - public function prev() + public function prev(): void { --$this->currentColumnIndex; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php index 697fc97efc3..a27daf092d9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php @@ -60,7 +60,7 @@ abstract class Dimension * * @param bool $pValue * - * @return Dimension + * @return $this */ public function setVisible($pValue) { @@ -85,9 +85,7 @@ abstract class Dimension * * @param int $pValue * - * @throws PhpSpreadsheetException - * - * @return Dimension + * @return $this */ public function setOutlineLevel($pValue) { @@ -115,7 +113,7 @@ abstract class Dimension * * @param bool $pValue * - * @return Dimension + * @return $this */ public function setCollapsed($pValue) { @@ -139,7 +137,7 @@ abstract class Dimension * * @param int $pValue * - * @return Dimension + * @return $this */ public function setXfIndex($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php index 8194da31e7d..1f1dae93abe 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php @@ -76,9 +76,7 @@ class Drawing extends BaseDrawing * @param string $pValue File path * @param bool $pVerifyFile Verify file * - * @throws PhpSpreadsheetException - * - * @return Drawing + * @return $this */ public function setPath($pValue, $pVerifyFile = true) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php index a1e05d60001..01ffed94d2f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php @@ -100,7 +100,7 @@ class Shadow implements IComparable * * @param bool $pValue * - * @return Shadow + * @return $this */ public function setVisible($pValue) { @@ -124,7 +124,7 @@ class Shadow implements IComparable * * @param int $pValue * - * @return Shadow + * @return $this */ public function setBlurRadius($pValue) { @@ -148,7 +148,7 @@ class Shadow implements IComparable * * @param int $pValue * - * @return Shadow + * @return $this */ public function setDistance($pValue) { @@ -172,7 +172,7 @@ class Shadow implements IComparable * * @param int $pValue * - * @return Shadow + * @return $this */ public function setDirection($pValue) { @@ -196,7 +196,7 @@ class Shadow implements IComparable * * @param int $pValue * - * @return Shadow + * @return $this */ public function setAlignment($pValue) { @@ -220,9 +220,9 @@ class Shadow implements IComparable * * @param Color $pValue * - * @return Shadow + * @return $this */ - public function setColor(Color $pValue = null) + public function setColor(?Color $pValue = null) { $this->color = $pValue; @@ -244,7 +244,7 @@ class Shadow implements IComparable * * @param int $pValue * - * @return Shadow + * @return $this */ public function setAlpha($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php index a78f4fccaeb..cc37e7f5df9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php @@ -172,7 +172,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setOddHeader($pValue) { @@ -196,7 +196,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setOddFooter($pValue) { @@ -220,7 +220,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setEvenHeader($pValue) { @@ -244,7 +244,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setEvenFooter($pValue) { @@ -268,7 +268,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setFirstHeader($pValue) { @@ -292,7 +292,7 @@ class HeaderFooter * * @param string $pValue * - * @return HeaderFooter + * @return $this */ public function setFirstFooter($pValue) { @@ -316,7 +316,7 @@ class HeaderFooter * * @param bool $pValue * - * @return HeaderFooter + * @return $this */ public function setDifferentOddEven($pValue) { @@ -340,7 +340,7 @@ class HeaderFooter * * @param bool $pValue * - * @return HeaderFooter + * @return $this */ public function setDifferentFirst($pValue) { @@ -364,7 +364,7 @@ class HeaderFooter * * @param bool $pValue * - * @return HeaderFooter + * @return $this */ public function setScaleWithDocument($pValue) { @@ -388,7 +388,7 @@ class HeaderFooter * * @param bool $pValue * - * @return HeaderFooter + * @return $this */ public function setAlignWithMargins($pValue) { @@ -400,10 +400,9 @@ class HeaderFooter /** * Add header/footer image. * - * @param HeaderFooterDrawing $image * @param string $location * - * @return HeaderFooter + * @return $this */ public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT) { @@ -417,7 +416,7 @@ class HeaderFooter * * @param string $location * - * @return HeaderFooter + * @return $this */ public function removeImage($location = self::IMAGE_HEADER_LEFT) { @@ -433,7 +432,7 @@ class HeaderFooter * * @param HeaderFooterDrawing[] $images * - * @return HeaderFooter + * @return $this */ public function setImages(array $images) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php index d8797a34736..6cfed37a475 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php @@ -22,8 +22,6 @@ class Iterator implements \Iterator /** * Create a new worksheet iterator. - * - * @param Spreadsheet $subject */ public function __construct(Spreadsheet $subject) { @@ -36,13 +34,13 @@ class Iterator implements \Iterator */ public function __destruct() { - unset($this->subject); + $this->subject = null; } /** * Rewind iterator. */ - public function rewind() + public function rewind(): void { $this->position = 0; } @@ -70,7 +68,7 @@ class Iterator implements \Iterator /** * Next value. */ - public function next() + public function next(): void { ++$this->position; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php index 6012e93e6ea..fb002114fd9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet; +use GdImage; + class MemoryDrawing extends BaseDrawing { // Rendering functions @@ -19,7 +21,7 @@ class MemoryDrawing extends BaseDrawing /** * Image resource. * - * @var resource + * @var GdImage|resource */ private $imageResource; @@ -53,7 +55,7 @@ class MemoryDrawing extends BaseDrawing $this->imageResource = null; $this->renderingFunction = self::RENDERING_DEFAULT; $this->mimeType = self::MIMETYPE_DEFAULT; - $this->uniqueName = md5(rand(0, 9999) . time() . rand(0, 9999)); + $this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999)); // Initialize parent parent::__construct(); @@ -62,7 +64,7 @@ class MemoryDrawing extends BaseDrawing /** * Get image resource. * - * @return resource + * @return GdImage|resource */ public function getImageResource() { @@ -72,9 +74,9 @@ class MemoryDrawing extends BaseDrawing /** * Set image resource. * - * @param resource $value + * @param GdImage|resource $value * - * @return MemoryDrawing + * @return $this */ public function setImageResource($value) { @@ -104,7 +106,7 @@ class MemoryDrawing extends BaseDrawing * * @param string $value see self::RENDERING_* * - * @return MemoryDrawing + * @return $this */ public function setRenderingFunction($value) { @@ -128,7 +130,7 @@ class MemoryDrawing extends BaseDrawing * * @param string $value see self::MIMETYPE_* * - * @return MemoryDrawing + * @return $this */ public function setMimeType($value) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php index eb44a1094c5..a8297933ade 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php @@ -68,7 +68,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setLeft($pValue) { @@ -92,7 +92,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setRight($pValue) { @@ -116,7 +116,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setTop($pValue) { @@ -140,7 +140,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setBottom($pValue) { @@ -164,7 +164,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setHeader($pValue) { @@ -188,7 +188,7 @@ class PageMargins * * @param float $pValue * - * @return PageMargins + * @return $this */ public function setFooter($pValue) { @@ -211,4 +211,34 @@ class PageMargins } } } + + public static function fromCentimeters(float $value): float + { + return $value / 2.54; + } + + public static function toCentimeters(float $value): float + { + return $value * 2.54; + } + + public static function fromMillimeters(float $value): float + { + return $value / 25.4; + } + + public static function toMillimeters(float $value): float + { + return $value * 25.4; + } + + public static function fromPoints(float $value): float + { + return $value / 72; + } + + public static function toPoints(float $value): float + { + return $value * 72; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php index ab007f61d54..d1a22a7b0a5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -76,10 +76,6 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; * 67 = A3 transverse paper (297 mm by 420 mm) * 68 = A3 extra transverse paper (322 mm by 445 mm) * - * - * @category PhpSpreadsheet - * - * @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) */ class PageSetup { @@ -160,6 +156,9 @@ class PageSetup const SETPRINTRANGE_OVERWRITE = 'O'; const SETPRINTRANGE_INSERT = 'I'; + const PAGEORDER_OVER_THEN_DOWN = 'overThenDown'; + const PAGEORDER_DOWN_THEN_OVER = 'downThenOver'; + /** * Paper size. * @@ -180,7 +179,7 @@ class PageSetup * Print scaling. Valid values range from 10 to 400 * This setting is overridden when fitToWidth and/or fitToHeight are in use * - * @var int? + * @var null|int */ private $scale = 100; @@ -196,7 +195,7 @@ class PageSetup * Fit To Height * Number of vertical pages to fit on. * - * @var int? + * @var null|int */ private $fitToHeight = 1; @@ -204,7 +203,7 @@ class PageSetup * Fit To Width * Number of horizontal pages to fit on. * - * @var int? + * @var null|int */ private $fitToWidth = 1; @@ -250,6 +249,8 @@ class PageSetup */ private $firstPageNumber; + private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER; + /** * Create a new PageSetup. */ @@ -272,7 +273,7 @@ class PageSetup * * @param int $pValue see self::PAPERSIZE_* * - * @return PageSetup + * @return $this */ public function setPaperSize($pValue) { @@ -296,7 +297,7 @@ class PageSetup * * @param string $pValue see self::ORIENTATION_* * - * @return PageSetup + * @return $this */ public function setOrientation($pValue) { @@ -308,7 +309,7 @@ class PageSetup /** * Get Scale. * - * @return int? + * @return null|int */ public function getScale() { @@ -323,9 +324,7 @@ class PageSetup * @param null|int $pValue * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth * - * @throws PhpSpreadsheetException - * - * @return PageSetup + * @return $this */ public function setScale($pValue, $pUpdate = true) { @@ -358,7 +357,7 @@ class PageSetup * * @param bool $pValue * - * @return PageSetup + * @return $this */ public function setFitToPage($pValue) { @@ -370,7 +369,7 @@ class PageSetup /** * Get Fit To Height. * - * @return int? + * @return null|int */ public function getFitToHeight() { @@ -383,7 +382,7 @@ class PageSetup * @param null|int $pValue * @param bool $pUpdate Update fitToPage so it applies rather than scaling * - * @return PageSetup + * @return $this */ public function setFitToHeight($pValue, $pUpdate = true) { @@ -398,7 +397,7 @@ class PageSetup /** * Get Fit To Width. * - * @return int? + * @return null|int */ public function getFitToWidth() { @@ -411,7 +410,7 @@ class PageSetup * @param null|int $pValue * @param bool $pUpdate Update fitToPage so it applies rather than scaling * - * @return PageSetup + * @return $this */ public function setFitToWidth($pValue, $pUpdate = true) { @@ -454,7 +453,7 @@ class PageSetup * * @param array $pValue Containing start column and end column, empty array if option unset * - * @return PageSetup + * @return $this */ public function setColumnsToRepeatAtLeft(array $pValue) { @@ -469,7 +468,7 @@ class PageSetup * @param string $pStart eg: 'A' * @param string $pEnd eg: 'B' * - * @return PageSetup + * @return $this */ public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd) { @@ -509,7 +508,7 @@ class PageSetup * * @param array $pValue Containing start column and end column, empty array if option unset * - * @return PageSetup + * @return $this */ public function setRowsToRepeatAtTop(array $pValue) { @@ -524,7 +523,7 @@ class PageSetup * @param int $pStart eg: 1 * @param int $pEnd eg: 1 * - * @return PageSetup + * @return $this */ public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd) { @@ -548,7 +547,7 @@ class PageSetup * * @param bool $value * - * @return PageSetup + * @return $this */ public function setHorizontalCentered($value) { @@ -572,7 +571,7 @@ class PageSetup * * @param bool $value * - * @return PageSetup + * @return $this */ public function setVerticalCentered($value) { @@ -589,8 +588,6 @@ class PageSetup * Otherwise, the specific range identified by the value of $index will be returned * Print areas are numbered from 1 * - * @throws PhpSpreadsheetException - * * @return string */ public function getPrintArea($index = 0) @@ -634,7 +631,7 @@ class PageSetup * Otherwise, the range identified by the value of $index will be removed from the series * Print areas are numbered from 1 * - * @return PageSetup + * @return $this */ public function clearPrintArea($index = 0) { @@ -669,9 +666,7 @@ class PageSetup * Default behaviour, or the "O" method, overwrites existing print area * The "I" method, inserts the new print area before any specified index, or at the end of the list * - * @throws PhpSpreadsheetException - * - * @return PageSetup + * @return $this */ public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE) { @@ -683,6 +678,9 @@ class PageSetup throw new PhpSpreadsheetException('Cell coordinate must not be absolute.'); } $value = strtoupper($value); + if (!$this->printArea) { + $index = 0; + } if ($method == self::SETPRINTRANGE_OVERWRITE) { if ($index == 0) { @@ -700,7 +698,7 @@ class PageSetup } } elseif ($method == self::SETPRINTRANGE_INSERT) { if ($index == 0) { - $this->printArea .= ($this->printArea == '') ? $value : ',' . $value; + $this->printArea = $this->printArea ? ($this->printArea . ',' . $value) : $value; } else { $printAreas = explode(',', $this->printArea); if ($index < 0) { @@ -730,9 +728,7 @@ class PageSetup * list. * Print areas are numbered from 1 * - * @throws PhpSpreadsheetException - * - * @return PageSetup + * @return $this */ public function addPrintArea($value, $index = -1) { @@ -760,9 +756,7 @@ class PageSetup * Default behaviour, or the "O" method, overwrites existing print area * The "I" method, inserts the new print area before any specified index, or at the end of the list * - * @throws PhpSpreadsheetException - * - * @return PageSetup + * @return $this */ public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE) { @@ -787,9 +781,7 @@ class PageSetup * list. * Print areas are numbered from 1 * - * @throws PhpSpreadsheetException - * - * @return PageSetup + * @return $this */ public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1) { @@ -815,7 +807,7 @@ class PageSetup * * @param int $value * - * @return PageSetup + * @return $this */ public function setFirstPageNumber($value) { @@ -827,13 +819,27 @@ class PageSetup /** * Reset first page number. * - * @return PageSetup + * @return $this */ public function resetFirstPageNumber() { return $this->setFirstPageNumber(null); } + public function getPageOrder(): string + { + return $this->pageOrder; + } + + public function setPageOrder(?string $pageOrder): self + { + if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) { + $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER; + } + + return $this; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php index 1815f45bf07..ba3af0a799e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php @@ -6,6 +6,17 @@ use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher; class Protection { + const ALGORITHM_MD2 = 'MD2'; + const ALGORITHM_MD4 = 'MD4'; + const ALGORITHM_MD5 = 'MD5'; + const ALGORITHM_SHA_1 = 'SHA-1'; + const ALGORITHM_SHA_256 = 'SHA-256'; + const ALGORITHM_SHA_384 = 'SHA-384'; + const ALGORITHM_SHA_512 = 'SHA-512'; + const ALGORITHM_RIPEMD_128 = 'RIPEMD-128'; + const ALGORITHM_RIPEMD_160 = 'RIPEMD-160'; + const ALGORITHM_WHIRLPOOL = 'WHIRLPOOL'; + /** * Sheet. * @@ -119,12 +130,33 @@ class Protection private $selectUnlockedCells = false; /** - * Password. + * Hashed password. * * @var string */ private $password = ''; + /** + * Algorithm name. + * + * @var string + */ + private $algorithm = ''; + + /** + * Salt value. + * + * @var string + */ + private $salt = ''; + + /** + * Spin count. + * + * @var int + */ + private $spinCount = 10000; + /** * Create a new Protection. */ @@ -172,7 +204,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setSheet($pValue) { @@ -196,7 +228,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setObjects($pValue) { @@ -220,7 +252,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setScenarios($pValue) { @@ -244,7 +276,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setFormatCells($pValue) { @@ -268,7 +300,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setFormatColumns($pValue) { @@ -292,7 +324,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setFormatRows($pValue) { @@ -316,7 +348,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setInsertColumns($pValue) { @@ -340,7 +372,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setInsertRows($pValue) { @@ -364,7 +396,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setInsertHyperlinks($pValue) { @@ -388,7 +420,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setDeleteColumns($pValue) { @@ -412,7 +444,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setDeleteRows($pValue) { @@ -436,7 +468,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setSelectLockedCells($pValue) { @@ -460,7 +492,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setSort($pValue) { @@ -484,7 +516,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setAutoFilter($pValue) { @@ -508,7 +540,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setPivotTables($pValue) { @@ -532,7 +564,7 @@ class Protection * * @param bool $pValue * - * @return Protection + * @return $this */ public function setSelectUnlockedCells($pValue) { @@ -542,7 +574,7 @@ class Protection } /** - * Get Password (hashed). + * Get hashed password. * * @return string */ @@ -557,18 +589,91 @@ class Protection * @param string $pValue * @param bool $pAlreadyHashed If the password has already been hashed, set this to true * - * @return Protection + * @return $this */ public function setPassword($pValue, $pAlreadyHashed = false) { if (!$pAlreadyHashed) { - $pValue = PasswordHasher::hashPassword($pValue); + $salt = $this->generateSalt(); + $this->setSalt($salt); + $pValue = PasswordHasher::hashPassword($pValue, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount()); } + $this->password = $pValue; return $this; } + /** + * Create a pseudorandom string. + */ + private function generateSalt(): string + { + return base64_encode(random_bytes(16)); + } + + /** + * Get algorithm name. + */ + public function getAlgorithm(): string + { + return $this->algorithm; + } + + /** + * Set algorithm name. + */ + public function setAlgorithm(string $algorithm): void + { + $this->algorithm = $algorithm; + } + + /** + * Get salt value. + */ + public function getSalt(): string + { + return $this->salt; + } + + /** + * Set salt value. + */ + public function setSalt(string $salt): void + { + $this->salt = $salt; + } + + /** + * Get spin count. + */ + public function getSpinCount(): int + { + return $this->spinCount; + } + + /** + * Set spin count. + */ + public function setSpinCount(int $spinCount): void + { + $this->spinCount = $spinCount; + } + + /** + * Verify that the given non-hashed password can "unlock" the protection. + */ + public function verify(string $password): bool + { + if (!$this->isProtectionEnabled()) { + return true; + } + + $hash = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount()); + + return $this->getPassword() === $hash; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php index 2a379d2cd34..4f48a34618b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php @@ -24,7 +24,7 @@ class Row * @param Worksheet $worksheet * @param int $rowIndex */ - public function __construct(Worksheet $worksheet = null, $rowIndex = 1) + public function __construct(?Worksheet $worksheet = null, $rowIndex = 1) { // Set parent and row index $this->worksheet = $worksheet; @@ -36,7 +36,7 @@ class Row */ public function __destruct() { - unset($this->worksheet); + $this->worksheet = null; } /** diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php index 8510d402a35..9b9d54eb611 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php @@ -43,7 +43,7 @@ class RowCellIterator extends CellIterator * @param string $startColumn The column address at which to start iterating * @param string $endColumn Optionally, the column address at which to stop iterating */ - public function __construct(Worksheet $worksheet = null, $rowIndex = 1, $startColumn = 'A', $endColumn = null) + public function __construct(?Worksheet $worksheet = null, $rowIndex = 1, $startColumn = 'A', $endColumn = null) { // Set subject and row index $this->worksheet = $worksheet; @@ -57,9 +57,7 @@ class RowCellIterator extends CellIterator * * @param string $startColumn The column address at which to start iterating * - * @throws PhpSpreadsheetException - * - * @return RowCellIterator + * @return $this */ public function resetStart($startColumn = 'A') { @@ -75,9 +73,7 @@ class RowCellIterator extends CellIterator * * @param string $endColumn The column address at which to stop iterating * - * @throws PhpSpreadsheetException - * - * @return RowCellIterator + * @return $this */ public function resetEnd($endColumn = null) { @@ -93,18 +89,18 @@ class RowCellIterator extends CellIterator * * @param string $column The column address to set the current pointer at * - * @throws PhpSpreadsheetException - * - * @return RowCellIterator + * @return $this */ public function seek($column = 'A') { + $columnx = $column; $column = Coordinate::columnIndexFromString($column); - if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) { - throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})"); - } elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) { + if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) { throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist'); } + if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) { + throw new PhpSpreadsheetException("Column $columnx is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})"); + } $this->currentColumnIndex = $column; return $this; @@ -113,7 +109,7 @@ class RowCellIterator extends CellIterator /** * Rewind the iterator to the starting column. */ - public function rewind() + public function rewind(): void { $this->currentColumnIndex = $this->startColumnIndex; } @@ -141,7 +137,7 @@ class RowCellIterator extends CellIterator /** * Set the iterator to its next value. */ - public function next() + public function next(): void { do { ++$this->currentColumnIndex; @@ -150,10 +146,8 @@ class RowCellIterator extends CellIterator /** * Set the iterator to its previous value. - * - * @throws PhpSpreadsheetException */ - public function prev() + public function prev(): void { do { --$this->currentColumnIndex; @@ -182,24 +176,16 @@ class RowCellIterator extends CellIterator /** * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary. - * - * @throws PhpSpreadsheetException */ - protected function adjustForExistingOnlyRange() + protected function adjustForExistingOnlyRange(): void { if ($this->onlyExistingCells) { while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) { ++$this->startColumnIndex; } - if ($this->startColumnIndex > $this->endColumnIndex) { - throw new PhpSpreadsheetException('No cells exist within the specified range'); - } while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) { --$this->endColumnIndex; } - if ($this->endColumnIndex < $this->startColumnIndex) { - throw new PhpSpreadsheetException('No cells exist within the specified range'); - } } } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php index e4346404d23..c4a87bdb558 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php @@ -56,7 +56,7 @@ class RowDimension extends Dimension * * @param int $pValue * - * @return RowDimension + * @return $this */ public function setRowIndex($pValue) { @@ -80,7 +80,7 @@ class RowDimension extends Dimension * * @param float $pValue * - * @return RowDimension + * @return $this */ public function setRowHeight($pValue) { @@ -104,7 +104,7 @@ class RowDimension extends Dimension * * @param bool $pValue * - * @return RowDimension + * @return $this */ public function setZeroHeight($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php index 433cea6a12a..4254253313b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php @@ -2,9 +2,10 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet; +use Iterator; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; -class RowIterator implements \Iterator +class RowIterator implements Iterator { /** * Worksheet to iterate. @@ -54,7 +55,7 @@ class RowIterator implements \Iterator */ public function __destruct() { - unset($this->subject); + $this->subject = null; } /** @@ -62,9 +63,7 @@ class RowIterator implements \Iterator * * @param int $startRow The row number at which to start iterating * - * @throws PhpSpreadsheetException - * - * @return RowIterator + * @return $this */ public function resetStart($startRow = 1) { @@ -86,7 +85,7 @@ class RowIterator implements \Iterator * * @param int $endRow The row number at which to stop iterating * - * @return RowIterator + * @return $this */ public function resetEnd($endRow = null) { @@ -100,9 +99,7 @@ class RowIterator implements \Iterator * * @param int $row The row number to set the current pointer at * - * @throws PhpSpreadsheetException - * - * @return RowIterator + * @return $this */ public function seek($row = 1) { @@ -117,7 +114,7 @@ class RowIterator implements \Iterator /** * Rewind the iterator to the starting row. */ - public function rewind() + public function rewind(): void { $this->position = $this->startRow; } @@ -145,7 +142,7 @@ class RowIterator implements \Iterator /** * Set the iterator to its next value. */ - public function next() + public function next(): void { ++$this->position; } @@ -153,7 +150,7 @@ class RowIterator implements \Iterator /** * Set the iterator to its previous value. */ - public function prev() + public function prev(): void { --$this->position; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php index 0a95f2139d8..2f7d3812be9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php @@ -77,9 +77,7 @@ class SheetView * * @param int $pValue * - * @throws PhpSpreadsheetException - * - * @return SheetView + * @return $this */ public function setZoomScale($pValue) { @@ -110,9 +108,7 @@ class SheetView * * @param int $pValue * - * @throws PhpSpreadsheetException - * - * @return SheetView + * @return $this */ public function setZoomScaleNormal($pValue) { @@ -130,7 +126,7 @@ class SheetView * * @param bool $pValue */ - public function setShowZeros($pValue) + public function setShowZeros($pValue): void { $this->showZeros = $pValue; } @@ -163,9 +159,7 @@ class SheetView * * @param string $pValue * - * @throws PhpSpreadsheetException - * - * @return SheetView + * @return $this */ public function setView($pValue) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php index f01632bce90..19833b71e50 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -13,9 +13,9 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Collection\Cells; use PhpOffice\PhpSpreadsheet\Collection\CellsFactory; use PhpOffice\PhpSpreadsheet\Comment; +use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\IComparable; -use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Shared; @@ -187,7 +187,7 @@ class Worksheet implements IComparable /** * Collection of merged cell ranges. * - * @var array + * @var string[] */ private $mergeCells = []; @@ -344,7 +344,7 @@ class Worksheet implements IComparable * @param Spreadsheet $parent * @param string $pTitle */ - public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet') + public function __construct(?Spreadsheet $parent = null, $pTitle = 'Worksheet') { // Set parent and title $this->parent = $parent; @@ -363,9 +363,9 @@ class Worksheet implements IComparable // Set sheet view $this->sheetView = new SheetView(); // Drawing collection - $this->drawingCollection = new \ArrayObject(); + $this->drawingCollection = new ArrayObject(); // Chart collection - $this->chartCollection = new \ArrayObject(); + $this->chartCollection = new ArrayObject(); // Protection $this->protection = new Protection(); // Default row dimension @@ -379,7 +379,7 @@ class Worksheet implements IComparable * Disconnect all cells from this Worksheet object, * typically so that the worksheet object can be unset. */ - public function disconnectCells() + public function disconnectCells(): void { if ($this->cellCollection !== null) { $this->cellCollection->unsetWorksheetCells(); @@ -424,8 +424,6 @@ class Worksheet implements IComparable * * @param string $pValue The string to check * - * @throws Exception - * * @return string The valid string */ private static function checkSheetCodeName($pValue) @@ -435,9 +433,11 @@ class Worksheet implements IComparable throw new Exception('Sheet code name cannot be empty.'); } // Some of the printable ASCII characters are invalid: * : / \ ? [ ] and first and last characters cannot be a "'" - if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) || + if ( + (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) || (Shared\StringHelper::substring($pValue, -1, 1) == '\'') || - (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) { + (Shared\StringHelper::substring($pValue, 0, 1) == '\'') + ) { throw new Exception('Invalid character found in sheet code name'); } @@ -454,8 +454,6 @@ class Worksheet implements IComparable * * @param string $pValue The string to check * - * @throws Exception - * * @return string The valid string */ private static function checkSheetTitle($pValue) @@ -556,7 +554,6 @@ class Worksheet implements IComparable /** * Add chart. * - * @param Chart $pChart * @param null|int $iChartIndex Index where chart should go (0,1,..., or null for last) * * @return Chart @@ -647,7 +644,7 @@ class Worksheet implements IComparable /** * Refresh column dimensions. * - * @return Worksheet + * @return $this */ public function refreshColumnDimensions() { @@ -666,7 +663,7 @@ class Worksheet implements IComparable /** * Refresh row dimensions. * - * @return Worksheet + * @return $this */ public function refreshRowDimensions() { @@ -707,7 +704,7 @@ class Worksheet implements IComparable /** * Calculate widths for auto-size columns. * - * @return Worksheet; + * @return $this */ public function calculateColumnWidths() { @@ -795,16 +792,14 @@ class Worksheet implements IComparable /** * Re-bind parent. * - * @param Spreadsheet $parent - * - * @return Worksheet + * @return $this */ public function rebindParent(Spreadsheet $parent) { if ($this->parent !== null) { - $namedRanges = $this->parent->getNamedRanges(); - foreach ($namedRanges as $namedRange) { - $parent->addNamedRange($namedRange); + $definedNames = $this->parent->getDefinedNames(); + foreach ($definedNames as $definedName) { + $parent->addDefinedName($definedName); } $this->parent->removeSheetByIndex( @@ -838,7 +833,7 @@ class Worksheet implements IComparable * @param bool $validate False to skip validation of new title. WARNING: This should only be set * at parse time (by Readers), where titles can be assumed to be valid. * - * @return Worksheet + * @return $this */ public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true) { @@ -913,7 +908,7 @@ class Worksheet implements IComparable * * @param string $value Sheet state (visible, hidden, veryHidden) * - * @return Worksheet + * @return $this */ public function setSheetState($value) { @@ -935,9 +930,7 @@ class Worksheet implements IComparable /** * Set page setup. * - * @param PageSetup $pValue - * - * @return Worksheet + * @return $this */ public function setPageSetup(PageSetup $pValue) { @@ -959,9 +952,7 @@ class Worksheet implements IComparable /** * Set page margins. * - * @param PageMargins $pValue - * - * @return Worksheet + * @return $this */ public function setPageMargins(PageMargins $pValue) { @@ -983,9 +974,7 @@ class Worksheet implements IComparable /** * Set page header/footer. * - * @param HeaderFooter $pValue - * - * @return Worksheet + * @return $this */ public function setHeaderFooter(HeaderFooter $pValue) { @@ -1007,9 +996,7 @@ class Worksheet implements IComparable /** * Set sheet view. * - * @param SheetView $pValue - * - * @return Worksheet + * @return $this */ public function setSheetView(SheetView $pValue) { @@ -1031,9 +1018,7 @@ class Worksheet implements IComparable /** * Set Protection. * - * @param Protection $pValue - * - * @return Worksheet + * @return $this */ public function setProtection(Protection $pValue) { @@ -1119,7 +1104,7 @@ class Worksheet implements IComparable * @param string $pCoordinate Coordinate of the cell, eg: 'A1' * @param mixed $pValue Value of the cell * - * @return Worksheet + * @return $this */ public function setCellValue($pCoordinate, $pValue) { @@ -1135,7 +1120,7 @@ class Worksheet implements IComparable * @param int $row Numeric row coordinate of the cell * @param mixed $value Value of the cell * - * @return Worksheet + * @return $this */ public function setCellValueByColumnAndRow($columnIndex, $row, $value) { @@ -1151,7 +1136,7 @@ class Worksheet implements IComparable * @param mixed $pValue Value of the cell * @param string $pDataType Explicit data type, see DataType::TYPE_* * - * @return Worksheet + * @return $this */ public function setCellValueExplicit($pCoordinate, $pValue, $pDataType) { @@ -1169,7 +1154,7 @@ class Worksheet implements IComparable * @param mixed $value Value of the cell * @param string $dataType Explicit data type, see DataType::TYPE_* * - * @return Worksheet + * @return $this */ public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType) { @@ -1185,8 +1170,6 @@ class Worksheet implements IComparable * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't * already exist, or a null should be returned instead * - * @throws Exception - * * @return null|Cell Cell that was found/created or null */ public function getCell($pCoordinate, $createIfNotExists = true) @@ -1207,11 +1190,13 @@ class Worksheet implements IComparable } // Named range? - if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) && - (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) { - $namedRange = NamedRange::resolveRange($pCoordinate, $this); + if ( + (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) && + (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $pCoordinate, $matches)) + ) { + $namedRange = DefinedName::resolveName($pCoordinate, $this); if ($namedRange !== null) { - $pCoordinate = $namedRange->getRange(); + $pCoordinate = $namedRange->getValue(); return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists); } @@ -1293,8 +1278,6 @@ class Worksheet implements IComparable * * @param string $pCoordinate Coordinate of the cell eg: 'A1' * - * @throws Exception - * * @return bool */ public function cellExists($pCoordinate) @@ -1307,11 +1290,13 @@ class Worksheet implements IComparable } // Named range? - if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) && - (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) { - $namedRange = NamedRange::resolveRange($pCoordinate, $this); + if ( + (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) && + (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/i', $pCoordinate, $matches)) + ) { + $namedRange = DefinedName::resolveName($pCoordinate, $this); if ($namedRange !== null) { - $pCoordinate = $namedRange->getRange(); + $pCoordinate = $namedRange->getValue(); if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) { if (!$namedRange->getLocalOnly()) { return $namedRange->getWorksheet()->cellExists($pCoordinate); @@ -1431,8 +1416,6 @@ class Worksheet implements IComparable * * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1' * - * @throws Exception - * * @return Style */ public function getStyle($pCellCoordinate) @@ -1480,7 +1463,7 @@ class Worksheet implements IComparable * * @param string $pCoordinate eg: 'A1' * - * @return Worksheet + * @return $this */ public function removeConditionalStyles($pCoordinate) { @@ -1505,7 +1488,7 @@ class Worksheet implements IComparable * @param string $pCoordinate eg: 'A1' * @param $pValue Conditional[] * - * @return Worksheet + * @return $this */ public function setConditionalStyles($pCoordinate, $pValue) { @@ -1543,9 +1526,7 @@ class Worksheet implements IComparable * @param Style $pCellStyle Cell style to duplicate * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1") * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function duplicateStyle(Style $pCellStyle, $pRange) { @@ -1588,9 +1569,7 @@ class Worksheet implements IComparable * @param Conditional[] $pCellStyle Cell style to duplicate * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1") * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function duplicateConditionalStyle(array $pCellStyle, $pRange = '') { @@ -1626,9 +1605,7 @@ class Worksheet implements IComparable * @param string $pCoordinate Cell coordinate (e.g. A1) * @param int $pBreak Break type (type of Worksheet::BREAK_*) * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function setBreak($pCoordinate, $pBreak) { @@ -1657,7 +1634,7 @@ class Worksheet implements IComparable * @param int $row Numeric row coordinate of the cell * @param int $break Break type (type of Worksheet::BREAK_*) * - * @return Worksheet + * @return $this */ public function setBreakByColumnAndRow($columnIndex, $row, $break) { @@ -1679,9 +1656,7 @@ class Worksheet implements IComparable * * @param string $pRange Cell range (e.g. A1:E1) * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function mergeCells($pRange) { @@ -1724,9 +1699,7 @@ class Worksheet implements IComparable * @param int $columnIndex2 Numeric column coordinate of the last cell * @param int $row2 Numeric row coordinate of the last cell * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2) { @@ -1740,9 +1713,7 @@ class Worksheet implements IComparable * * @param string $pRange Cell range (e.g. A1:E1) * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function unmergeCells($pRange) { @@ -1770,9 +1741,7 @@ class Worksheet implements IComparable * @param int $columnIndex2 Numeric column coordinate of the last cell * @param int $row2 Numeric row coordinate of the last cell * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2) { @@ -1784,7 +1753,7 @@ class Worksheet implements IComparable /** * Get merge cells array. * - * @return array[] + * @return string[] */ public function getMergeCells() { @@ -1795,9 +1764,9 @@ class Worksheet implements IComparable * Set merge cells array for the entire sheet. Use instead mergeCells() to merge * a single cell range. * - * @param array $pValue + * @param string[] $pValue * - * @return Worksheet + * @return $this */ public function setMergeCells(array $pValue) { @@ -1813,7 +1782,7 @@ class Worksheet implements IComparable * @param string $pPassword Password to unlock the protection * @param bool $pAlreadyHashed If the password has already been hashed, set this to true * - * @return Worksheet + * @return $this */ public function protectCells($pRange, $pPassword, $pAlreadyHashed = false) { @@ -1838,7 +1807,7 @@ class Worksheet implements IComparable * @param string $password Password to unlock the protection * @param bool $alreadyHashed If the password has already been hashed, set this to true * - * @return Worksheet + * @return $this */ public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false) { @@ -1852,9 +1821,7 @@ class Worksheet implements IComparable * * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1) * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function unprotectCells($pRange) { @@ -1878,9 +1845,7 @@ class Worksheet implements IComparable * @param int $columnIndex2 Numeric column coordinate of the last cell * @param int $row2 Numeric row coordinate of the last cell * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2) { @@ -1915,9 +1880,7 @@ class Worksheet implements IComparable * @param AutoFilter|string $pValue * A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function setAutoFilter($pValue) { @@ -1938,9 +1901,7 @@ class Worksheet implements IComparable * @param int $columnIndex2 Numeric column coordinate of the second cell * @param int $row2 Numeric row coordinate of the second cell * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2) { @@ -1954,7 +1915,7 @@ class Worksheet implements IComparable /** * Remove autofilter. * - * @return Worksheet + * @return $this */ public function removeAutoFilter() { @@ -1985,9 +1946,7 @@ class Worksheet implements IComparable * @param null|string $cell Position of the split * @param null|string $topLeftCell default position of the right bottom pane * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function freezePane($cell, $topLeftCell = null) { @@ -2012,7 +1971,7 @@ class Worksheet implements IComparable * @param int $columnIndex Numeric column coordinate of the cell * @param int $row Numeric row coordinate of the cell * - * @return Worksheet + * @return $this */ public function freezePaneByColumnAndRow($columnIndex, $row) { @@ -2022,7 +1981,7 @@ class Worksheet implements IComparable /** * Unfreeze Pane. * - * @return Worksheet + * @return $this */ public function unfreezePane() { @@ -2045,9 +2004,7 @@ class Worksheet implements IComparable * @param int $pBefore Insert before this one * @param int $pNumRows Number of rows to insert * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function insertNewRowBefore($pBefore, $pNumRows = 1) { @@ -2067,9 +2024,7 @@ class Worksheet implements IComparable * @param string $pBefore Insert before this one, eg: 'A' * @param int $pNumCols Number of columns to insert * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function insertNewColumnBefore($pBefore, $pNumCols = 1) { @@ -2089,9 +2044,7 @@ class Worksheet implements IComparable * @param int $beforeColumnIndex Insert before this one (numeric column coordinate of the cell) * @param int $pNumCols Number of columns to insert * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function insertNewColumnBeforeByIndex($beforeColumnIndex, $pNumCols = 1) { @@ -2108,28 +2061,31 @@ class Worksheet implements IComparable * @param int $pRow Remove starting with this one * @param int $pNumRows Number of rows to remove * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function removeRow($pRow, $pNumRows = 1) { - if ($pRow >= 1) { - for ($r = 0; $r < $pNumRows; ++$r) { - $this->getCellCollection()->removeRow($pRow + $r); - } - - $highestRow = $this->getHighestDataRow(); - $objReferenceHelper = ReferenceHelper::getInstance(); - $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this); - for ($r = 0; $r < $pNumRows; ++$r) { - $this->getCellCollection()->removeRow($highestRow); - --$highestRow; - } - } else { + if ($pRow < 1) { throw new Exception('Rows to be deleted should at least start from row 1.'); } + $highestRow = $this->getHighestDataRow(); + $removedRowsCounter = 0; + + for ($r = 0; $r < $pNumRows; ++$r) { + if ($pRow + $r <= $highestRow) { + $this->getCellCollection()->removeRow($pRow + $r); + ++$removedRowsCounter; + } + } + + $objReferenceHelper = ReferenceHelper::getInstance(); + $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this); + for ($r = 0; $r < $removedRowsCounter; ++$r) { + $this->getCellCollection()->removeRow($highestRow); + --$highestRow; + } + return $this; } @@ -2139,9 +2095,7 @@ class Worksheet implements IComparable * @param string $pColumn Remove starting with this one, eg: 'A' * @param int $pNumCols Number of columns to remove * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function removeColumn($pColumn, $pNumCols = 1) { @@ -2179,9 +2133,7 @@ class Worksheet implements IComparable * @param int $columnIndex Remove starting with this one (numeric column coordinate of the cell) * @param int $numColumns Number of columns to remove * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function removeColumnByIndex($columnIndex, $numColumns = 1) { @@ -2207,7 +2159,7 @@ class Worksheet implements IComparable * * @param bool $pValue Show gridlines (true/false) * - * @return Worksheet + * @return $this */ public function setShowGridlines($pValue) { @@ -2231,7 +2183,7 @@ class Worksheet implements IComparable * * @param bool $pValue Print gridlines (true/false) * - * @return Worksheet + * @return $this */ public function setPrintGridlines($pValue) { @@ -2255,7 +2207,7 @@ class Worksheet implements IComparable * * @param bool $pValue Show row and column headers (true/false) * - * @return Worksheet + * @return $this */ public function setShowRowColHeaders($pValue) { @@ -2279,7 +2231,7 @@ class Worksheet implements IComparable * * @param bool $pValue Show summary below (true/false) * - * @return Worksheet + * @return $this */ public function setShowSummaryBelow($pValue) { @@ -2303,7 +2255,7 @@ class Worksheet implements IComparable * * @param bool $pValue Show summary right (true/false) * - * @return Worksheet + * @return $this */ public function setShowSummaryRight($pValue) { @@ -2327,7 +2279,7 @@ class Worksheet implements IComparable * * @param Comment[] $pValue * - * @return Worksheet + * @return $this */ public function setComments(array $pValue) { @@ -2341,8 +2293,6 @@ class Worksheet implements IComparable * * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1' * - * @throws Exception - * * @return Comment */ public function getComment($pCellCoordinate) @@ -2408,7 +2358,7 @@ class Worksheet implements IComparable * * @param string $pCoordinate Cell (i.e. A1) * - * @return Worksheet + * @return $this */ public function setSelectedCell($pCoordinate) { @@ -2420,7 +2370,7 @@ class Worksheet implements IComparable * * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6' * - * @return Worksheet + * @return $this */ public function setSelectedCells($pCoordinate) { @@ -2456,9 +2406,7 @@ class Worksheet implements IComparable * @param int $columnIndex Numeric column coordinate of the cell * @param int $row Numeric row coordinate of the cell * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function setSelectedCellByColumnAndRow($columnIndex, $row) { @@ -2480,7 +2428,7 @@ class Worksheet implements IComparable * * @param bool $value Right-to-left true/false * - * @return Worksheet + * @return $this */ public function setRightToLeft($value) { @@ -2497,9 +2445,7 @@ class Worksheet implements IComparable * @param string $startCell Insert array starting from this cell address as the top left coordinate * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false) { @@ -2614,16 +2560,14 @@ class Worksheet implements IComparable * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero * True - Return rows and columns indexed by their actual row and column IDs * - * @throws Exception - * * @return array */ public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) { - $namedRange = NamedRange::resolveRange($pNamedRange, $this); + $namedRange = DefinedName::resolveName($pNamedRange, $this); if ($namedRange !== null) { $pWorkSheet = $namedRange->getWorksheet(); - $pCellRange = $namedRange->getRange(); + $pCellRange = $namedRange->getValue(); return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef); } @@ -2684,7 +2628,7 @@ class Worksheet implements IComparable /** * Run PhpSpreadsheet garbage collector. * - * @return Worksheet + * @return $this */ public function garbageCollect() { @@ -2782,11 +2726,10 @@ class Worksheet implements IComparable * Set hyperlink. * * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1' - * @param null|Hyperlink $pHyperlink * - * @return Worksheet + * @return $this */ - public function setHyperlink($pCellCoordinate, Hyperlink $pHyperlink = null) + public function setHyperlink($pCellCoordinate, ?Hyperlink $pHyperlink = null) { if ($pHyperlink === null) { unset($this->hyperlinkCollection[$pCellCoordinate]); @@ -2843,11 +2786,10 @@ class Worksheet implements IComparable * Set data validation. * * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1' - * @param null|DataValidation $pDataValidation * - * @return Worksheet + * @return $this */ - public function setDataValidation($pCellCoordinate, DataValidation $pDataValidation = null) + public function setDataValidation($pCellCoordinate, ?DataValidation $pDataValidation = null) { if ($pDataValidation === null) { unset($this->dataValidationCollection[$pCellCoordinate]); @@ -2933,12 +2875,12 @@ class Worksheet implements IComparable /** * Reset tab color. * - * @return Worksheet + * @return $this */ public function resetTabColor() { $this->tabColor = null; - unset($this->tabColor); + $this->tabColor = null; return $this; } @@ -2956,7 +2898,7 @@ class Worksheet implements IComparable /** * Copy worksheet (!= clone!). * - * @return Worksheet + * @return static */ public function copy() { @@ -3005,9 +2947,7 @@ class Worksheet implements IComparable * @param bool $validate False to skip validation of new title. WARNING: This should only be set * at parse time (by Readers), where titles can be assumed to be valid. * - * @throws Exception - * - * @return Worksheet + * @return $this */ public function setCodeName($pValue, $validate = true) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php index f13150d7eb3..afda5c433ed 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php @@ -35,6 +35,16 @@ abstract class BaseWriter implements IWriter */ private $diskCachingDirectory = './'; + /** + * @var resource + */ + protected $fileHandle; + + /** + * @var bool + */ + private $shouldCloseFile; + public function getIncludeCharts() { return $this->includeCharts; @@ -83,4 +93,39 @@ abstract class BaseWriter implements IWriter { return $this->diskCachingDirectory; } + + /** + * Open file handle. + * + * @param resource|string $filename + */ + public function openFileHandle($filename): void + { + if (is_resource($filename)) { + $this->fileHandle = $filename; + $this->shouldCloseFile = false; + + return; + } + + $fileHandle = $filename ? fopen($filename, 'wb+') : false; + if ($fileHandle === false) { + throw new Exception('Could not open file "' . $filename . '" for writing.'); + } + + $this->fileHandle = $fileHandle; + $this->shouldCloseFile = true; + } + + /** + * Close file handle only if we opened it ourselves. + */ + protected function maybeCloseFileHandle(): void + { + if ($this->shouldCloseFile) { + if (!fclose($this->fileHandle)) { + throw new Exception('Could not close file after writing.'); + } + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php index ae38ab73218..74f286361b9 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php @@ -77,11 +77,9 @@ class Csv extends BaseWriter /** * Save PhpSpreadsheet to file. * - * @param string $pFilename - * - * @throws Exception + * @param resource|string $pFilename */ - public function save($pFilename) + public function save($pFilename): void { // Fetch sheet $sheet = $this->spreadsheet->getSheet($this->sheetIndex); @@ -92,10 +90,7 @@ class Csv extends BaseWriter Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE); // Open file - $fileHandle = fopen($pFilename, 'wb+'); - if ($fileHandle === false) { - throw new Exception("Could not open file $pFilename for writing."); - } + $this->openFileHandle($pFilename); if ($this->excelCompatibility) { $this->setUseBOM(true); // Enforce UTF-8 BOM Header @@ -104,13 +99,15 @@ class Csv extends BaseWriter $this->setDelimiter(';'); // Set delimiter to a semi-colon $this->setLineEnding("\r\n"); } + if ($this->useBOM) { // Write the UTF-8 BOM code if required - fwrite($fileHandle, "\xEF\xBB\xBF"); + fwrite($this->fileHandle, "\xEF\xBB\xBF"); } + if ($this->includeSeparatorLine) { // Write the separator line if required - fwrite($fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding); + fwrite($this->fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding); } // Identify the range that we need to extract from the worksheet @@ -122,12 +119,10 @@ class Csv extends BaseWriter // Convert the row to an array... $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas); // ... and write to the file - $this->writeLine($fileHandle, $cellsArray[0]); + $this->writeLine($this->fileHandle, $cellsArray[0]); } - // Close file - fclose($fileHandle); - + $this->maybeCloseFileHandle(); Calculation::setArrayReturnType($saveArrayReturnType); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); } @@ -147,7 +142,7 @@ class Csv extends BaseWriter * * @param string $pValue Delimiter, defaults to ',' * - * @return CSV + * @return $this */ public function setDelimiter($pValue) { @@ -171,13 +166,10 @@ class Csv extends BaseWriter * * @param string $pValue Enclosure, defaults to " * - * @return CSV + * @return $this */ - public function setEnclosure($pValue) + public function setEnclosure($pValue = '"') { - if ($pValue == '') { - $pValue = null; - } $this->enclosure = $pValue; return $this; @@ -198,7 +190,7 @@ class Csv extends BaseWriter * * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL) * - * @return CSV + * @return $this */ public function setLineEnding($pValue) { @@ -222,7 +214,7 @@ class Csv extends BaseWriter * * @param bool $pValue Use UTF-8 byte-order mark? Defaults to false * - * @return CSV + * @return $this */ public function setUseBOM($pValue) { @@ -246,7 +238,7 @@ class Csv extends BaseWriter * * @param bool $pValue Use separator line? Defaults to false * - * @return CSV + * @return $this */ public function setIncludeSeparatorLine($pValue) { @@ -271,7 +263,7 @@ class Csv extends BaseWriter * @param bool $pValue Set the file to be written as a fully Excel compatible csv file * Note that this overrides other settings such as useBOM, enclosure and delimiter * - * @return CSV + * @return $this */ public function setExcelCompatibility($pValue) { @@ -295,7 +287,7 @@ class Csv extends BaseWriter * * @param int $pValue Sheet index * - * @return CSV + * @return $this */ public function setSheetIndex($pValue) { @@ -304,33 +296,51 @@ class Csv extends BaseWriter return $this; } + private $enclosureRequired = true; + + public function setEnclosureRequired(bool $value): self + { + $this->enclosureRequired = $value; + + return $this; + } + + public function getEnclosureRequired(): bool + { + return $this->enclosureRequired; + } + /** * Write line to CSV file. * * @param resource $pFileHandle PHP filehandle * @param array $pValues Array containing values in a row */ - private function writeLine($pFileHandle, array $pValues) + private function writeLine($pFileHandle, array $pValues): void { // No leading delimiter - $writeDelimiter = false; + $delimiter = ''; // Build the line $line = ''; foreach ($pValues as $element) { - // Escape enclosures - $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element); - // Add delimiter - if ($writeDelimiter) { - $line .= $this->delimiter; - } else { - $writeDelimiter = true; + $line .= $delimiter; + $delimiter = $this->delimiter; + // Escape enclosures + $enclosure = $this->enclosure; + if ($enclosure) { + // If enclosure is not required, use enclosure only if + // element contains newline, delimiter, or enclosure. + if (!$this->enclosureRequired && strpbrk($element, "$delimiter$enclosure\n") === false) { + $enclosure = ''; + } else { + $element = str_replace($enclosure, $enclosure . $enclosure, $element); + } } - // Add enclosed string - $line .= $this->enclosure . $element . $this->enclosure; + $line .= $enclosure . $element . $enclosure; } // Add line ending diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php index 0f55f2f4039..e9de2ce688c 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer; +use HTMLPurifier; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; @@ -23,7 +24,6 @@ use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; class Html extends BaseWriter { @@ -133,9 +133,14 @@ class Html extends BaseWriter private $generateSheetNavigationBlock = true; /** - * Create a new HTML. + * Callback for editing generated html. * - * @param Spreadsheet $spreadsheet + * @var null|callable + */ + private $editHtmlCallback; + + /** + * Create a new HTML. */ public function __construct(Spreadsheet $spreadsheet) { @@ -146,11 +151,26 @@ class Html extends BaseWriter /** * Save Spreadsheet to file. * - * @param string $pFilename - * - * @throws WriterException + * @param resource|string $pFilename */ - public function save($pFilename) + public function save($pFilename): void + { + // Open file + $this->openFileHandle($pFilename); + + // Write html + fwrite($this->fileHandle, $this->generateHTMLAll()); + + // Close file + $this->maybeCloseFileHandle(); + } + + /** + * Save Spreadsheet as html to variable. + * + * @return string + */ + public function generateHtmlAll() { // garbage collect $this->spreadsheet->garbageCollect(); @@ -163,33 +183,50 @@ class Html extends BaseWriter // Build CSS $this->buildCSS(!$this->useInlineCss); - // Open file - $fileHandle = fopen($pFilename, 'wb+'); - if ($fileHandle === false) { - throw new WriterException("Could not open file $pFilename for writing."); - } + $html = ''; // Write headers - fwrite($fileHandle, $this->generateHTMLHeader(!$this->useInlineCss)); + $html .= $this->generateHTMLHeader(!$this->useInlineCss); // Write navigation (tabs) if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) { - fwrite($fileHandle, $this->generateNavigation()); + $html .= $this->generateNavigation(); } // Write data - fwrite($fileHandle, $this->generateSheetData()); + $html .= $this->generateSheetData(); // Write footer - fwrite($fileHandle, $this->generateHTMLFooter()); - - // Close file - fclose($fileHandle); + $html .= $this->generateHTMLFooter(); + $callback = $this->editHtmlCallback; + if ($callback) { + $html = $callback($html); + } Calculation::setArrayReturnType($saveArrayReturnType); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); + + return $html; } + /** + * Set a callback to edit the entire HTML. + * + * The callback must accept the HTML as string as first parameter, + * and it must return the edited HTML as string. + */ + public function setEditHtmlCallback(?callable $callback): void + { + $this->editHtmlCallback = $callback; + } + + const VALIGN_ARR = [ + Alignment::VERTICAL_BOTTOM => 'bottom', + Alignment::VERTICAL_TOP => 'top', + Alignment::VERTICAL_CENTER => 'middle', + Alignment::VERTICAL_JUSTIFY => 'middle', + ]; + /** * Map VAlign. * @@ -199,45 +236,44 @@ class Html extends BaseWriter */ private function mapVAlign($vAlign) { - switch ($vAlign) { - case Alignment::VERTICAL_BOTTOM: - return 'bottom'; - case Alignment::VERTICAL_TOP: - return 'top'; - case Alignment::VERTICAL_CENTER: - case Alignment::VERTICAL_JUSTIFY: - return 'middle'; - default: - return 'baseline'; - } + return array_key_exists($vAlign, self::VALIGN_ARR) ? self::VALIGN_ARR[$vAlign] : 'baseline'; } + const HALIGN_ARR = [ + Alignment::HORIZONTAL_LEFT => 'left', + Alignment::HORIZONTAL_RIGHT => 'right', + Alignment::HORIZONTAL_CENTER => 'center', + Alignment::HORIZONTAL_CENTER_CONTINUOUS => 'center', + Alignment::HORIZONTAL_JUSTIFY => 'justify', + ]; + /** * Map HAlign. * * @param string $hAlign Horizontal alignment * - * @return false|string + * @return string */ private function mapHAlign($hAlign) { - switch ($hAlign) { - case Alignment::HORIZONTAL_GENERAL: - return false; - case Alignment::HORIZONTAL_LEFT: - return 'left'; - case Alignment::HORIZONTAL_RIGHT: - return 'right'; - case Alignment::HORIZONTAL_CENTER: - case Alignment::HORIZONTAL_CENTER_CONTINUOUS: - return 'center'; - case Alignment::HORIZONTAL_JUSTIFY: - return 'justify'; - default: - return false; - } + return array_key_exists($hAlign, self::HALIGN_ARR) ? self::HALIGN_ARR[$hAlign] : ''; } + const BORDER_ARR = [ + Border::BORDER_NONE => 'none', + Border::BORDER_DASHDOT => '1px dashed', + Border::BORDER_DASHDOTDOT => '1px dotted', + Border::BORDER_DASHED => '1px dashed', + Border::BORDER_DOTTED => '1px dotted', + Border::BORDER_DOUBLE => '3px double', + Border::BORDER_HAIR => '1px solid', + Border::BORDER_MEDIUM => '2px solid', + Border::BORDER_MEDIUMDASHDOT => '2px dashed', + Border::BORDER_MEDIUMDASHDOTDOT => '2px dotted', + Border::BORDER_SLANTDASHDOT => '2px dashed', + Border::BORDER_THICK => '3px solid', + ]; + /** * Map border style. * @@ -247,39 +283,7 @@ class Html extends BaseWriter */ private function mapBorderStyle($borderStyle) { - switch ($borderStyle) { - case Border::BORDER_NONE: - return 'none'; - case Border::BORDER_DASHDOT: - return '1px dashed'; - case Border::BORDER_DASHDOTDOT: - return '1px dotted'; - case Border::BORDER_DASHED: - return '1px dashed'; - case Border::BORDER_DOTTED: - return '1px dotted'; - case Border::BORDER_DOUBLE: - return '3px double'; - case Border::BORDER_HAIR: - return '1px solid'; - case Border::BORDER_MEDIUM: - return '2px solid'; - case Border::BORDER_MEDIUMDASHDOT: - return '2px dashed'; - case Border::BORDER_MEDIUMDASHDOTDOT: - return '2px dotted'; - case Border::BORDER_MEDIUMDASHED: - return '2px dashed'; - case Border::BORDER_SLANTDASHDOT: - return '2px dashed'; - case Border::BORDER_THICK: - return '3px solid'; - case Border::BORDER_THIN: - return '1px solid'; - default: - // map others to thin - return '1px solid'; - } + return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid'; } /** @@ -297,7 +301,7 @@ class Html extends BaseWriter * * @param int $pValue Sheet index * - * @return HTML + * @return $this */ public function setSheetIndex($pValue) { @@ -321,7 +325,7 @@ class Html extends BaseWriter * * @param bool $pValue Flag indicating whether the sheet navigation block should be generated or not * - * @return HTML + * @return $this */ public function setGenerateSheetNavigationBlock($pValue) { @@ -332,6 +336,8 @@ class Html extends BaseWriter /** * Write all sheets (resets sheetIndex to NULL). + * + * @return $this */ public function writeAllSheets() { @@ -340,55 +346,38 @@ class Html extends BaseWriter return $this; } + private static function generateMeta($val, $desc) + { + return $val ? (' ' . PHP_EOL) : ''; + } + /** * Generate HTML header. * * @param bool $pIncludeStyles Include styles? * - * @throws WriterException - * * @return string */ public function generateHTMLHeader($pIncludeStyles = false) { // Construct HTML $properties = $this->spreadsheet->getProperties(); - $html = '' . PHP_EOL; - $html .= '' . PHP_EOL; + $html = '' . PHP_EOL; + $html .= '' . PHP_EOL; $html .= ' ' . PHP_EOL; - $html .= ' ' . PHP_EOL; - $html .= ' ' . PHP_EOL; - if ($properties->getTitle() > '') { - $html .= ' ' . htmlspecialchars($properties->getTitle()) . '' . PHP_EOL; - } - if ($properties->getCreator() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getTitle() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getDescription() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getSubject() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getKeywords() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getCategory() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getCompany() > '') { - $html .= ' ' . PHP_EOL; - } - if ($properties->getManager() > '') { - $html .= ' ' . PHP_EOL; - } + $html .= ' ' . PHP_EOL; + $html .= ' ' . PHP_EOL; + $html .= ' ' . htmlspecialchars($properties->getTitle()) . '' . PHP_EOL; + $html .= self::generateMeta($properties->getCreator(), 'author'); + $html .= self::generateMeta($properties->getTitle(), 'title'); + $html .= self::generateMeta($properties->getDescription(), 'description'); + $html .= self::generateMeta($properties->getSubject(), 'subject'); + $html .= self::generateMeta($properties->getKeywords(), 'keywords'); + $html .= self::generateMeta($properties->getCategory(), 'category'); + $html .= self::generateMeta($properties->getCompany(), 'company'); + $html .= self::generateMeta($properties->getManager(), 'manager'); - if ($pIncludeStyles) { - $html .= $this->generateStyles(true); - } + $html .= $pIncludeStyles ? $this->generateStyles(true) : $this->generatePageDeclarations(true); $html .= ' ' . PHP_EOL; $html .= '' . PHP_EOL; @@ -397,27 +386,61 @@ class Html extends BaseWriter return $html; } + private function generateSheetPrep() + { + // Ensure that Spans have been calculated? + $this->calculateSpans(); + + // Fetch sheets + if ($this->sheetIndex === null) { + $sheets = $this->spreadsheet->getAllSheets(); + } else { + $sheets = [$this->spreadsheet->getSheet($this->sheetIndex)]; + } + + return $sheets; + } + + private function generateSheetStarts($sheet, $rowMin) + { + // calculate start of , + $tbodyStart = $rowMin; + $theadStart = $theadEnd = 0; // default: no no + if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) { + $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop(); + + // we can only support repeating rows that start at top row + if ($rowsToRepeatAtTop[0] == 1) { + $theadStart = $rowsToRepeatAtTop[0]; + $theadEnd = $rowsToRepeatAtTop[1]; + $tbodyStart = $rowsToRepeatAtTop[1] + 1; + } + } + + return [$theadStart, $theadEnd, $tbodyStart]; + } + + private function generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart) + { + // ? + $startTag = ($row == $theadStart) ? (' ' . PHP_EOL) : ''; + if (!$startTag) { + $startTag = ($row == $tbodyStart) ? (' ' . PHP_EOL) : ''; + } + $endTag = ($row == $theadEnd) ? (' ' . PHP_EOL) : ''; + $cellType = ($row >= $tbodyStart) ? 'td' : 'th'; + + return [$cellType, $startTag, $endTag]; + } + /** * Generate sheet data. * - * @throws WriterException - * * @return string */ public function generateSheetData() { - // Ensure that Spans have been calculated? - if ($this->sheetIndex !== null || !$this->spansAreCalculated) { - $this->calculateSpans(); - } - - // Fetch sheets - $sheets = []; - if ($this->sheetIndex === null) { - $sheets = $this->spreadsheet->getAllSheets(); - } else { - $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex); - } + $sheets = $this->generateSheetPrep(); // Construct HTML $html = ''; @@ -429,52 +452,27 @@ class Html extends BaseWriter $html .= $this->generateTableHeader($sheet); // Get worksheet dimension - $dimension = explode(':', $sheet->calculateWorksheetDimension()); - $dimension[0] = Coordinate::coordinateFromString($dimension[0]); - $dimension[0][0] = Coordinate::columnIndexFromString($dimension[0][0]); - $dimension[1] = Coordinate::coordinateFromString($dimension[1]); - $dimension[1][0] = Coordinate::columnIndexFromString($dimension[1][0]); + [$min, $max] = explode(':', $sheet->calculateWorksheetDataDimension()); + [$minCol, $minRow] = Coordinate::coordinateFromString($min); + $minCol = Coordinate::columnIndexFromString($minCol); + [$maxCol, $maxRow] = Coordinate::coordinateFromString($max); + $maxCol = Coordinate::columnIndexFromString($maxCol); - // row min,max - $rowMin = $dimension[0][1]; - $rowMax = $dimension[1][1]; - - // calculate start of , - $tbodyStart = $rowMin; - $theadStart = $theadEnd = 0; // default: no no - if ($sheet->getPageSetup()->isRowsToRepeatAtTopSet()) { - $rowsToRepeatAtTop = $sheet->getPageSetup()->getRowsToRepeatAtTop(); - - // we can only support repeating rows that start at top row - if ($rowsToRepeatAtTop[0] == 1) { - $theadStart = $rowsToRepeatAtTop[0]; - $theadEnd = $rowsToRepeatAtTop[1]; - $tbodyStart = $rowsToRepeatAtTop[1] + 1; - } - } + [$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $minRow); // Loop through cells - $row = $rowMin - 1; - while ($row++ < $rowMax) { - // ? - if ($row == $theadStart) { - $html .= ' ' . PHP_EOL; - $cellType = 'th'; - } - - // ? - if ($row == $tbodyStart) { - $html .= ' ' . PHP_EOL; - $cellType = 'td'; - } + $row = $minRow - 1; + while ($row++ < $maxRow) { + [$cellType, $startTag, $endTag] = $this->generateSheetTags($row, $theadStart, $theadEnd, $tbodyStart); + $html .= $startTag; // Write row if there are HTML table cells in it if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) { // Start a new rowData $rowData = []; // Loop through columns - $column = $dimension[0][0]; - while ($column <= $dimension[1][0]) { + $column = $minCol; + while ($column <= $maxCol) { // Cell exists? if ($sheet->cellExistsByColumnAndRow($column, $row)) { $rowData[$column] = Coordinate::stringFromColumnIndex($column) . $row; @@ -486,23 +484,16 @@ class Html extends BaseWriter $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType); } - // ? - if ($row == $theadEnd) { - $html .= ' ' . PHP_EOL; - } + $html .= $endTag; } $html .= $this->extendRowsForChartsAndImages($sheet, $row); - // Close table body. - $html .= ' ' . PHP_EOL; - // Write table footer $html .= $this->generateTableFooter(); - // Writing PDF? - if ($this->isPdf) { + if ($this->isPdf && $this->useInlineCss) { if ($this->sheetIndex === null && $sheetId + 1 < $this->spreadsheet->getSheetCount()) { - $html .= '
      '; + $html .= '
      '; } } @@ -516,8 +507,6 @@ class Html extends BaseWriter /** * Generate sheet tabs. * - * @throws WriterException - * * @return string */ public function generateNavigation() @@ -551,13 +540,30 @@ class Html extends BaseWriter return $html; } - private function extendRowsForChartsAndImages(Worksheet $pSheet, $row) + /** + * Extend Row if chart is placed after nominal end of row. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. + * + * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet + * @param int $row Row to check for charts + * + * @return array + * + * @codeCoverageIgnore + */ + private function extendRowsForCharts(Worksheet $pSheet, int $row) { $rowMax = $row; $colMax = 'A'; + $anyfound = false; if ($this->includeCharts) { foreach ($pSheet->getChartCollection() as $chart) { if ($chart instanceof Chart) { + $anyfound = true; $chartCoordinates = $chart->getTopLeftPosition(); $chartTL = Coordinate::coordinateFromString($chartCoordinates['cell']); $chartCol = Coordinate::columnIndexFromString($chartTL[0]); @@ -571,44 +577,68 @@ class Html extends BaseWriter } } + return [$rowMax, $colMax, $anyfound]; + } + + private function extendRowsForChartsAndImages(Worksheet $pSheet, int $row): string + { + [$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($pSheet, $row); + foreach ($pSheet->getDrawingCollection() as $drawing) { - if ($drawing instanceof Drawing) { - $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates()); - $imageCol = Coordinate::columnIndexFromString($imageTL[0]); - if ($imageTL[1] > $rowMax) { - $rowMax = $imageTL[1]; - if ($imageCol > Coordinate::columnIndexFromString($colMax)) { - $colMax = $imageTL[0]; - } + $anyfound = true; + $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates()); + $imageCol = Coordinate::columnIndexFromString($imageTL[0]); + if ($imageTL[1] > $rowMax) { + $rowMax = $imageTL[1]; + if ($imageCol > Coordinate::columnIndexFromString($colMax)) { + $colMax = $imageTL[0]; } } } // Don't extend rows if not needed - if ($row === $rowMax) { + if ($row === $rowMax || !$anyfound) { return ''; } $html = ''; ++$colMax; - + ++$row; while ($row <= $rowMax) { $html .= ''; for ($col = 'A'; $col != $colMax; ++$col) { - $html .= ''; - $html .= $this->writeImageInCell($pSheet, $col . $row); - if ($this->includeCharts) { - $html .= $this->writeChartInCell($pSheet, $col . $row); + $htmlx = $this->writeImageInCell($pSheet, $col . $row); + $htmlx .= $this->includeCharts ? $this->writeChartInCell($pSheet, $col . $row) : ''; + if ($htmlx) { + $html .= "$htmlx"; + } else { + $html .= ""; } - $html .= ''; } ++$row; - $html .= ''; + $html .= '' . PHP_EOL; } return $html; } + /** + * Convert Windows file name to file protocol URL. + * + * @param string $filename file name on local system + * + * @return string + */ + public static function winFileToUrl($filename) + { + // Windows filename + if (substr($filename, 1, 2) === ':\\') { + $filename = 'file:///' . str_replace('\\', '/', $filename); + } + + return $filename; + } + /** * Generate image tag in cell. * @@ -624,57 +654,44 @@ class Html extends BaseWriter // Write images foreach ($pSheet->getDrawingCollection() as $drawing) { + if ($drawing->getCoordinates() != $coordinates) { + continue; + } + $filedesc = $drawing->getDescription(); + $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image'; if ($drawing instanceof Drawing) { - if ($drawing->getCoordinates() == $coordinates) { - $filename = $drawing->getPath(); + $filename = $drawing->getPath(); - // Strip off eventual '.' - if (substr($filename, 0, 1) == '.') { - $filename = substr($filename, 1); - } + // Strip off eventual '.' + $filename = preg_replace('/^[.]/', '', $filename); - // Prepend images root - $filename = $this->getImagesRoot() . $filename; + // Prepend images root + $filename = $this->getImagesRoot() . $filename; - // Strip off eventual '.' - if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') { - $filename = substr($filename, 1); - } + // Strip off eventual '.' if followed by non-/ + $filename = preg_replace('@^[.]([^/])@', '$1', $filename); - // Convert UTF8 data to PCDATA - $filename = htmlspecialchars($filename); + // Convert UTF8 data to PCDATA + $filename = htmlspecialchars($filename); - $html .= PHP_EOL; - if ((!$this->embedImages) || ($this->isPdf)) { - $imageData = $filename; - } else { + $html .= PHP_EOL; + $imageData = self::winFileToUrl($filename); + + if ($this->embedImages && !$this->isPdf) { + $picture = @file_get_contents($filename); + if ($picture !== false) { $imageDetails = getimagesize($filename); - if ($fp = fopen($filename, 'rb', 0)) { - $picture = ''; - while (!feof($fp)) { - $picture .= fread($fp, 1024); - } - fclose($fp); - // base64 encode the binary data, then break it - // into chunks according to RFC 2045 semantics - $base64 = chunk_split(base64_encode($picture)); - $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; - } else { - $imageData = $filename; - } + // base64 encode the binary data + $base64 = base64_encode($picture); + $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; } + } - $html .= '
      '; - $html .= ''; - $html .= '
      '; - } + $html .= '' . $filedesc . ''; } elseif ($drawing instanceof MemoryDrawing) { - if ($drawing->getCoordinates() != $coordinates) { - continue; - } ob_start(); // Let's start output buffering. imagepng($drawing->getImageResource()); // This will normally output the image, but because of ob_start(), it won't. $contents = ob_get_contents(); // Instead, output above is saved to $contents @@ -686,7 +703,7 @@ class Html extends BaseWriter // max-width: 100% ensures that image doesnt overflow containing cell // width: X sets width of supplied image. // As a result, images bigger than cell will be contained and images smaller will not get stretched - $html .= ''; + $html .= '' . $filedesc . ''; } } @@ -695,11 +712,18 @@ class Html extends BaseWriter /** * Generate chart tag in cell. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. * * @param Worksheet $pSheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet * @param string $coordinates Cell coordinates * * @return string + * + * @codeCoverageIgnore */ private function writeChartInCell(Worksheet $pSheet, $coordinates) { @@ -718,17 +742,17 @@ class Html extends BaseWriter $html .= PHP_EOL; $imageDetails = getimagesize($chartFileName); + $filedesc = $chart->getTitle(); + $filedesc = $filedesc ? self::getChartCaption($filedesc->getCaption()) : ''; + $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart'; if ($fp = fopen($chartFileName, 'rb', 0)) { $picture = fread($fp, filesize($chartFileName)); fclose($fp); - // base64 encode the binary data, then break it - // into chunks according to RFC 2045 semantics - $base64 = chunk_split(base64_encode($picture)); + // base64 encode the binary data + $base64 = base64_encode($picture); $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64; - $html .= '
      '; - $html .= '' . PHP_EOL; - $html .= '
      '; + $html .= '' . $filedesc . '' . PHP_EOL; unlink($chartFileName); } @@ -740,13 +764,32 @@ class Html extends BaseWriter return $html; } + /** + * Extend Row if chart is placed after nominal end of row. + * This code should be exercised by sample: + * Chart/32_Chart_read_write_PDF.php. + * However, that test is suppressed due to out-of-date + * Jpgraph code issuing warnings. So, don't measure + * code coverage for this function till that is fixed. + * Caption is described in documentation as fixed, + * but in 32_Chart it is somehow an array of RichText. + * + * @param mixed $cap + * + * @return string + * + * @codeCoverageIgnore + */ + private static function getChartCaption($cap) + { + return is_array($cap) ? implode(' ', $cap) : $cap; + } + /** * Generate CSS styles. * * @param bool $generateSurroundingHTML Generate surrounding HTML tags? (<style> and </style>) * - * @throws WriterException - * * @return string */ public function generateStyles($generateSurroundingHTML = true) @@ -760,7 +803,7 @@ class Html extends BaseWriter // Start styles if ($generateSurroundingHTML) { $html .= ' \n"; + if (!in_array($baseCell, $adjustedBaseCells)) { + // subtract rowspan by 1 + --$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan']; + $adjustedBaseCells[] = $baseCell; + } + } + } + } } /** @@ -1669,7 +1781,6 @@ class Html extends BaseWriter * * @see https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092 * - * @param Worksheet $pSheet * @param string $coordinate * * @return string @@ -1678,11 +1789,81 @@ class Html extends BaseWriter { $result = ''; if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) { - $result .= ''; - $result .= '
      ' . nl2br($pSheet->getComment($coordinate)->getText()->getPlainText()) . '
      '; - $result .= PHP_EOL; + $sanitizer = new HTMLPurifier(); + $sanitizedString = $sanitizer->purify($pSheet->getComment($coordinate)->getText()->getPlainText()); + if ($sanitizedString !== '') { + $result .= ''; + $result .= '
      ' . nl2br($sanitizedString) . '
      '; + $result .= PHP_EOL; + } } return $result; } + + /** + * Generate @page declarations. + * + * @param bool $generateSurroundingHTML + * + * @return string + */ + private function generatePageDeclarations($generateSurroundingHTML) + { + // Ensure that Spans have been calculated? + $this->calculateSpans(); + + // Fetch sheets + $sheets = []; + if ($this->sheetIndex === null) { + $sheets = $this->spreadsheet->getAllSheets(); + } else { + $sheets[] = $this->spreadsheet->getSheet($this->sheetIndex); + } + + // Construct HTML + $htmlPage = $generateSurroundingHTML ? ('' . PHP_EOL) : ''; + + return $htmlPage; + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php index 448b532fc2a..5129d65583e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php @@ -8,8 +8,6 @@ interface IWriter { /** * IWriter constructor. - * - * @param Spreadsheet $spreadsheet */ public function __construct(Spreadsheet $spreadsheet); @@ -59,9 +57,7 @@ interface IWriter /** * Save PhpSpreadsheet to file. * - * @param string $pFilename Name of the file to save - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @param resource|string $pFilename Name of the file to save */ public function save($pFilename); @@ -78,8 +74,6 @@ interface IWriter * @param bool $pValue * @param string $pDirectory Disk caching directory * - * @throws Exception when directory does not exist - * * @return IWriter */ public function setUseDiskCaching($pValue, $pDirectory = null); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php index 83fc2931db5..36f3e9caf8b 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php @@ -12,7 +12,9 @@ use PhpOffice\PhpSpreadsheet\Writer\Ods\Mimetype; use PhpOffice\PhpSpreadsheet\Writer\Ods\Settings; use PhpOffice\PhpSpreadsheet\Writer\Ods\Styles; use PhpOffice\PhpSpreadsheet\Writer\Ods\Thumbnails; -use ZipArchive; +use ZipStream\Exception\OverflowException; +use ZipStream\Option\Archive; +use ZipStream\ZipStream; class Ods extends BaseWriter { @@ -32,8 +34,6 @@ class Ods extends BaseWriter /** * Create a new Ods. - * - * @param Spreadsheet $spreadsheet */ public function __construct(Spreadsheet $spreadsheet) { @@ -73,11 +73,9 @@ class Ods extends BaseWriter /** * Save PhpSpreadsheet to file. * - * @param string $pFilename - * - * @throws WriterException + * @param resource|string $pFilename */ - public function save($pFilename) + public function save($pFilename): void { if (!$this->spreadSheet) { throw new WriterException('PhpSpreadsheet object unassigned.'); @@ -86,71 +84,51 @@ class Ods extends BaseWriter // garbage collect $this->spreadSheet->garbageCollect(); - // If $pFilename is php://output or php://stdout, make it a temporary file... - $originalFilename = $pFilename; - if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { - $pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp'); - if ($pFilename == '') { - $pFilename = $originalFilename; - } - } + $this->openFileHandle($pFilename); - $zip = $this->createZip($pFilename); + $zip = $this->createZip(); - $zip->addFromString('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest()); - $zip->addFromString('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail()); - $zip->addFromString('content.xml', $this->getWriterPart('content')->write()); - $zip->addFromString('meta.xml', $this->getWriterPart('meta')->write()); - $zip->addFromString('mimetype', $this->getWriterPart('mimetype')->write()); - $zip->addFromString('settings.xml', $this->getWriterPart('settings')->write()); - $zip->addFromString('styles.xml', $this->getWriterPart('styles')->write()); + $zip->addFile('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest()); + $zip->addFile('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail()); + $zip->addFile('content.xml', $this->getWriterPart('content')->write()); + $zip->addFile('meta.xml', $this->getWriterPart('meta')->write()); + $zip->addFile('mimetype', $this->getWriterPart('mimetype')->write()); + $zip->addFile('settings.xml', $this->getWriterPart('settings')->write()); + $zip->addFile('styles.xml', $this->getWriterPart('styles')->write()); // Close file - if ($zip->close() === false) { - throw new WriterException("Could not close zip file $pFilename."); + try { + $zip->finish(); + } catch (OverflowException $e) { + throw new WriterException('Could not close resource.'); } - // If a temporary file was used, copy it to the correct file stream - if ($originalFilename != $pFilename) { - if (copy($pFilename, $originalFilename) === false) { - throw new WriterException("Could not copy temporary zip file $pFilename to $originalFilename."); - } - @unlink($pFilename); - } + $this->maybeCloseFileHandle(); } /** * Create zip object. * - * @param string $pFilename - * - * @throws WriterException - * - * @return ZipArchive + * @return ZipStream */ - private function createZip($pFilename) + private function createZip() { - // Create new ZIP file and open it for writing - $zip = new ZipArchive(); - - if (file_exists($pFilename)) { - unlink($pFilename); - } // Try opening the ZIP file - if ($zip->open($pFilename, ZipArchive::OVERWRITE) !== true) { - if ($zip->open($pFilename, ZipArchive::CREATE) !== true) { - throw new WriterException("Could not open $pFilename for writing."); - } + if (!is_resource($this->fileHandle)) { + throw new WriterException('Could not open resource for writing.'); } - return $zip; + // Create new ZIP stream + $options = new Archive(); + $options->setEnableZip64(false); + $options->setOutputStream($this->fileHandle); + + return new ZipStream(null, $options); } /** * Get Spreadsheet object. * - * @throws WriterException - * * @return Spreadsheet */ public function getSpreadsheet() @@ -167,7 +145,7 @@ class Ods extends BaseWriter * * @param Spreadsheet $spreadsheet PhpSpreadsheet object * - * @return self + * @return $this */ public function setSpreadsheet(Spreadsheet $spreadsheet) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php index 2f543be51b8..b0829bf1de1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php @@ -6,14 +6,11 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; /** - * @category PhpSpreadsheet - * - * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) * @author Alexander Pervakov */ class Comment { - public static function write(XMLWriter $objWriter, Cell $cell) + public static function write(XMLWriter $objWriter, Cell $cell): void { $comments = $cell->getWorksheet()->getComments(); if (!isset($comments[$cell->getCoordinate()])) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php index dea5100f595..96e66850df1 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php @@ -16,11 +16,6 @@ use PhpOffice\PhpSpreadsheet\Writer\Ods; use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment; /** - * @category PhpSpreadsheet - * - * @method Ods getParentWriter - * - * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) * @author Alexander Pervakov */ class Content extends WriterPart @@ -29,11 +24,21 @@ class Content extends WriterPart const NUMBER_ROWS_REPEATED_MAX = 1048576; const CELL_STYLE_PREFIX = 'ce'; + private $formulaConvertor; + + /** + * Set parent Ods writer. + */ + public function __construct(Ods $writer) + { + parent::__construct($writer); + + $this->formulaConvertor = new Formula($this->getParentWriter()->getSpreadsheet()->getDefinedNames()); + } + /** * Write content.xml to XML format. * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function write() @@ -98,7 +103,9 @@ class Content extends WriterPart $this->writeSheets($objWriter); - $objWriter->writeElement('table:named-expressions'); + // Defined names (ranges and formulae) + (new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write(); + $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); @@ -108,13 +115,10 @@ class Content extends WriterPart /** * Write sheets. - * - * @param XMLWriter $objWriter */ - private function writeSheets(XMLWriter $objWriter) + private function writeSheets(XMLWriter $objWriter): void { - $spreadsheet = $this->getParentWriter()->getSpreadsheet(); // @var $spreadsheet Spreadsheet - + $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */ $sheetCount = $spreadsheet->getSheetCount(); for ($i = 0; $i < $sheetCount; ++$i) { $objWriter->startElement('table:table'); @@ -130,11 +134,8 @@ class Content extends WriterPart /** * Write rows of the specified sheet. - * - * @param XMLWriter $objWriter - * @param Worksheet $sheet */ - private function writeRows(XMLWriter $objWriter, Worksheet $sheet) + private function writeRows(XMLWriter $objWriter, Worksheet $sheet): void { $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX; $span_row = 0; @@ -166,13 +167,8 @@ class Content extends WriterPart /** * Write cells of the specified row. - * - * @param XMLWriter $objWriter - * @param Row $row - * - * @throws Exception */ - private function writeCells(XMLWriter $objWriter, Row $row) + private function writeCells(XMLWriter $objWriter, Row $row): void { $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX; $prevColumn = -1; @@ -212,7 +208,7 @@ class Content extends WriterPart // don't do anything } } - $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue()); + $objWriter->writeAttribute('table:formula', $this->formulaConvertor->convertFormula($cell->getValue())); if (is_numeric($formulaValue)) { $objWriter->writeAttribute('office:value-type', 'float'); } else { @@ -258,11 +254,10 @@ class Content extends WriterPart /** * Write span. * - * @param XMLWriter $objWriter * @param int $curColumn * @param int $prevColumn */ - private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn) + private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn): void { $diff = $curColumn - $prevColumn - 1; if (1 === $diff) { @@ -276,11 +271,8 @@ class Content extends WriterPart /** * Write XF cell styles. - * - * @param XMLWriter $writer - * @param Spreadsheet $spreadsheet */ - private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet) + private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void { foreach ($spreadsheet->getCellXfCollection() as $style) { $writer->startElement('style:style'); @@ -370,13 +362,8 @@ class Content extends WriterPart /** * Write attributes for merged cell. - * - * @param XMLWriter $objWriter - * @param Cell $cell - * - * @throws \PhpOffice\PhpSpreadsheet\Exception */ - private function writeCellMerge(XMLWriter $objWriter, Cell $cell) + private function writeCellMerge(XMLWriter $objWriter, Cell $cell): void { if (!$cell->isMergeRangeValueCell()) { return; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php new file mode 100644 index 00000000000..db766fb42e7 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php @@ -0,0 +1,119 @@ +definedNames[] = $definedName->getName(); + } + } + + public function convertFormula(string $formula, string $worksheetName = ''): string + { + $formula = $this->convertCellReferences($formula, $worksheetName); + $formula = $this->convertDefinedNames($formula); + + if (substr($formula, 0, 1) !== '=') { + $formula = '=' . $formula; + } + + return 'of:' . $formula; + } + + private function convertDefinedNames(string $formula): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '/mui', + $formula, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $lengths = array_map('strlen', array_column($splitRanges[0], 0)); + $offsets = array_column($splitRanges[0], 1); + $values = array_column($splitRanges[0], 0); + + while ($splitCount > 0) { + --$splitCount; + $length = $lengths[$splitCount]; + $offset = $offsets[$splitCount]; + $value = $values[$splitCount]; + + if (in_array($value, $this->definedNames, true)) { + $formula = substr($formula, 0, $offset) . '$$' . $value . substr($formula, $offset + $length); + } + } + + return $formula; + } + + private function convertCellReferences(string $formula, string $worksheetName): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui', + $formula, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $lengths = array_map('strlen', array_column($splitRanges[0], 0)); + $offsets = array_column($splitRanges[0], 1); + + $worksheets = $splitRanges[2]; + $columns = $splitRanges[6]; + $rows = $splitRanges[7]; + + // Replace any commas in the formula with semi-colons for Ods + // If by chance there are commas in worksheet names, then they will be "fixed" again in the loop + // because we've already extracted worksheet names with our preg_match_all() + $formula = str_replace(',', ';', $formula); + while ($splitCount > 0) { + --$splitCount; + $length = $lengths[$splitCount]; + $offset = $offsets[$splitCount]; + $worksheet = $worksheets[$splitCount][0]; + $column = $columns[$splitCount][0]; + $row = $rows[$splitCount][0]; + + $newRange = ''; + if (empty($worksheet)) { + if (($offset === 0) || ($formula[$offset - 1] !== ':')) { + // We need a worksheet + $worksheet = $worksheetName; + } + } else { + $worksheet = str_replace("''", "'", trim($worksheet, "'")); + } + if (!empty($worksheet)) { + $newRange = "['" . str_replace("'", "''", $worksheet) . "'"; + } elseif (substr($formula, $offset - 1, 1) !== ':') { + $newRange = '['; + } + $newRange .= '.'; + + if (!empty($column)) { + $newRange .= $column; + } + if (!empty($row)) { + $newRange .= $row; + } + // close the wrapping [] unless this is the first part of a range + $newRange .= substr($formula, $offset + $length, 1) !== ':' ? ']' : ''; + + $formula = substr($formula, 0, $offset) . $newRange . substr($formula, $offset + $length); + } + + return $formula; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php index ffe5eff7925..365221f77da 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php @@ -12,11 +12,9 @@ class Meta extends WriterPart * * @param Spreadsheet $spreadsheet * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ - public function write(Spreadsheet $spreadsheet = null) + public function write(?Spreadsheet $spreadsheet = null) { if (!$spreadsheet) { $spreadsheet = $this->getParentWriter()->getSpreadsheet(); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php index 1ec9d1eb44b..c9085cf8ce7 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/MetaInf.php @@ -9,8 +9,6 @@ class MetaInf extends WriterPart /** * Write META-INF/manifest.xml to XML format. * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeManifest() diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php index d0fed2b379b..4aac3685255 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Mimetype.php @@ -13,7 +13,7 @@ class Mimetype extends WriterPart * * @return string XML Output */ - public function write(Spreadsheet $spreadsheet = null) + public function write(?Spreadsheet $spreadsheet = null) { return 'application/vnd.oasis.opendocument.spreadsheet'; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php new file mode 100644 index 00000000000..9edc5c6448c --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php @@ -0,0 +1,126 @@ +objWriter = $objWriter; + $this->spreadsheet = $spreadsheet; + $this->formulaConvertor = $formulaConvertor; + } + + public function write(): void + { + $this->objWriter->startElement('table:named-expressions'); + $this->writeExpressions(); + $this->objWriter->endElement(); + } + + private function writeExpressions(): void + { + $definedNames = $this->spreadsheet->getDefinedNames(); + + foreach ($definedNames as $definedName) { + if ($definedName->isFormula()) { + $this->objWriter->startElement('table:named-expression'); + $this->writeNamedFormula($definedName, $this->spreadsheet->getActiveSheet()); + } else { + $this->objWriter->startElement('table:named-range'); + $this->writeNamedRange($definedName); + } + + $this->objWriter->endElement(); + } + } + + private function writeNamedFormula(DefinedName $definedName, Worksheet $defaultWorksheet): void + { + $this->objWriter->writeAttribute('table:name', $definedName->getName()); + $this->objWriter->writeAttribute( + 'table:expression', + $this->formulaConvertor->convertFormula($definedName->getValue(), $definedName->getWorksheet()->getTitle()) + ); + $this->objWriter->writeAttribute('table:base-cell-address', $this->convertAddress( + $definedName, + "'" . (($definedName->getWorksheet() !== null) ? $definedName->getWorksheet()->getTitle() : $defaultWorksheet->getTitle()) . "'!\$A\$1" + )); + } + + private function writeNamedRange(DefinedName $definedName): void + { + $this->objWriter->writeAttribute('table:name', $definedName->getName()); + $this->objWriter->writeAttribute('table:base-cell-address', $this->convertAddress( + $definedName, + "'" . $definedName->getWorksheet()->getTitle() . "'!\$A\$1" + )); + $this->objWriter->writeAttribute('table:cell-range-address', $this->convertAddress($definedName, $definedName->getValue())); + } + + private function convertAddress(DefinedName $definedName, string $address): string + { + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui', + $address, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $lengths = array_map('strlen', array_column($splitRanges[0], 0)); + $offsets = array_column($splitRanges[0], 1); + + $worksheets = $splitRanges[2]; + $columns = $splitRanges[6]; + $rows = $splitRanges[7]; + + while ($splitCount > 0) { + --$splitCount; + $length = $lengths[$splitCount]; + $offset = $offsets[$splitCount]; + $worksheet = $worksheets[$splitCount][0]; + $column = $columns[$splitCount][0]; + $row = $rows[$splitCount][0]; + + $newRange = ''; + if (empty($worksheet)) { + if (($offset === 0) || ($address[$offset - 1] !== ':')) { + // We need a worksheet + $worksheet = $definedName->getWorksheet()->getTitle(); + } + } else { + $worksheet = str_replace("''", "'", trim($worksheet, "'")); + } + if (!empty($worksheet)) { + $newRange = "'" . str_replace("'", "''", $worksheet) . "'."; + } + + if (!empty($column)) { + $newRange .= $column; + } + if (!empty($row)) { + $newRange .= $row; + } + + $address = substr($address, 0, $offset) . $newRange . substr($address, $offset + $length); + } + + if (substr($address, 0, 1) === '=') { + $address = substr($address, 1); + } + + return $address; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php index 18f7ee6d5cb..d458e8c2bdc 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php @@ -12,11 +12,9 @@ class Settings extends WriterPart * * @param Spreadsheet $spreadsheet * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ - public function write(Spreadsheet $spreadsheet = null) + public function write(?Spreadsheet $spreadsheet = null) { $objWriter = null; if ($this->getParentWriter()->getUseDiskCaching()) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php index eaf5cad9526..7ba7eba7356 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Styles.php @@ -12,11 +12,9 @@ class Styles extends WriterPart * * @param Spreadsheet $spreadsheet * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ - public function write(Spreadsheet $spreadsheet = null) + public function write(?Spreadsheet $spreadsheet = null) { $objWriter = null; if ($this->getParentWriter()->getUseDiskCaching()) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php index a29a14adbe8..dfab0654c61 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Thumbnails.php @@ -13,7 +13,7 @@ class Thumbnails extends WriterPart * * @return string XML Output */ - public function writeThumbnail(Spreadsheet $spreadsheet = null) + public function writeThumbnail(?Spreadsheet $spreadsheet = null) { return ''; } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php index 3e2f968416d..1982c4506ba 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php @@ -25,8 +25,6 @@ abstract class WriterPart /** * Set parent Ods writer. - * - * @param Ods $writer */ public function __construct(Ods $writer) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php index b80083ae466..872204589ff 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Writer; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; @@ -38,13 +37,6 @@ abstract class Pdf extends Html */ protected $paperSize; - /** - * Temporary storage for Save Array Return type. - * - * @var string - */ - private $saveArrayReturnType; - /** * Paper Sizes xRef List. * @@ -127,8 +119,9 @@ abstract class Pdf extends Html public function __construct(Spreadsheet $spreadsheet) { parent::__construct($spreadsheet); - $this->setUseInlineCss(true); - $this->tempDir = File::sysGetTempDir(); + //$this->setUseInlineCss(true); + $this->tempDir = File::sysGetTempDir() . '/phpsppdf'; + $this->isPdf = true; } /** @@ -150,7 +143,7 @@ abstract class Pdf extends Html * * @param string $fontName * - * @return Pdf + * @return $this */ public function setFont($fontName) { @@ -222,8 +215,6 @@ abstract class Pdf extends Html * * @param string $pValue Temporary storage directory * - * @throws WriterException when directory does not exist - * * @return self */ public function setTempDir($pValue) @@ -242,42 +233,21 @@ abstract class Pdf extends Html * * @param string $pFilename Name of the file to save as * - * @throws WriterException - * * @return resource */ protected function prepareForSave($pFilename) { - // garbage collect - $this->spreadsheet->garbageCollect(); - - $this->saveArrayReturnType = Calculation::getArrayReturnType(); - Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE); - // Open file - $fileHandle = fopen($pFilename, 'w'); - if ($fileHandle === false) { - throw new WriterException("Could not open file $pFilename for writing."); - } + $this->openFileHandle($pFilename); - // Set PDF - $this->isPdf = true; - // Build CSS - $this->buildCSS(true); - - return $fileHandle; + return $this->fileHandle; } /** * Save PhpSpreadsheet to PDF file, post-save. - * - * @param resource $fileHandle */ - protected function restoreStateAfterSave($fileHandle) + protected function restoreStateAfterSave(): void { - // Close file - fclose($fileHandle); - - Calculation::setArrayReturnType($this->saveArrayReturnType); + $this->maybeCloseFileHandle(); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php index 3c3044d711b..9ae2ccee358 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php @@ -21,10 +21,8 @@ class Dompdf extends Pdf * Save Spreadsheet to file. * * @param string $pFilename Name of the file to save as - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename) + public function save($pFilename): void { $fileHandle = parent::prepareForSave($pFilename); @@ -63,16 +61,12 @@ class Dompdf extends Pdf $pdf = $this->createExternalWriterInstance(); $pdf->setPaper(strtolower($paperSize), $orientation); - $pdf->loadHtml( - $this->generateHTMLHeader(false) . - $this->generateSheetData() . - $this->generateHTMLFooter() - ); + $pdf->loadHtml($this->generateHTMLAll()); $pdf->render(); // Write to file fwrite($fileHandle, $pdf->output()); - parent::restoreStateAfterSave($fileHandle); + parent::restoreStateAfterSave(); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php index fd2664a8238..75e0010d5fa 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Pdf; -use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Writer\Pdf; @@ -24,11 +23,8 @@ class Mpdf extends Pdf * Save Spreadsheet to file. * * @param string $pFilename Name of the file to save as - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * @throws PhpSpreadsheetException */ - public function save($pFilename) + public function save($pFilename): void { $fileHandle = parent::prepareForSave($pFilename); @@ -65,7 +61,7 @@ class Mpdf extends Pdf } // Create PDF - $config = ['tempDir' => $this->tempDir]; + $config = ['tempDir' => $this->tempDir . '/mpdf']; $pdf = $this->createExternalWriterInstance($config); $ortmp = $orientation; $pdf->_setPageSize(strtoupper($paperSize), $ortmp); @@ -85,17 +81,15 @@ class Mpdf extends Pdf $pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords()); $pdf->SetCreator($this->spreadsheet->getProperties()->getCreator()); - $pdf->WriteHTML($this->generateHTMLHeader(false)); - $html = $this->generateSheetData(); + $html = $this->generateHTMLAll(); foreach (\array_chunk(\explode(PHP_EOL, $html), 1000) as $lines) { $pdf->WriteHTML(\implode(PHP_EOL, $lines)); } - $pdf->WriteHTML($this->generateHTMLFooter()); // Write to file fwrite($fileHandle, $pdf->Output('', 'S')); - parent::restoreStateAfterSave($fileHandle); + parent::restoreStateAfterSave(); } /** diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php index 8a97b8fed5d..7530b1efe0e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php @@ -2,11 +2,23 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Pdf; +use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Writer\Pdf; class Tcpdf extends Pdf { + /** + * Create a new PDF Writer instance. + * + * @param Spreadsheet $spreadsheet Spreadsheet object + */ + public function __construct(Spreadsheet $spreadsheet) + { + parent::__construct($spreadsheet); + $this->setUseInlineCss(true); + } + /** * Gets the implementation of external PDF library that should be used. * @@ -25,10 +37,8 @@ class Tcpdf extends Pdf * Save Spreadsheet to file. * * @param string $pFilename Name of the file to save as - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - public function save($pFilename) + public function save($pFilename): void { $fileHandle = parent::prepareForSave($pFilename); @@ -77,11 +87,7 @@ class Tcpdf extends Pdf // Set the appropriate font $pdf->SetFont($this->getFont()); - $pdf->writeHTML( - $this->generateHTMLHeader(false) . - $this->generateSheetData() . - $this->generateHTMLFooter() - ); + $pdf->writeHTML($this->generateHTMLAll()); // Document info $pdf->SetTitle($this->spreadsheet->getProperties()->getTitle()); @@ -93,6 +99,6 @@ class Tcpdf extends Pdf // Write to file fwrite($fileHandle, $pdf->output($pFilename, 'S')); - parent::restoreStateAfterSave($fileHandle); + parent::restoreStateAfterSave(); } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php new file mode 100644 index 00000000000..c7c2e7d6058 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php @@ -0,0 +1,901 @@ +spreadsheet = $spreadsheet; + + $this->parser = new Xls\Parser($spreadsheet); + } + + /** + * Save Spreadsheet to file. + * + * @param resource|string $pFilename + */ + public function save($pFilename): void + { + // garbage collect + $this->spreadsheet->garbageCollect(); + + $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog(); + Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false); + $saveDateReturnType = Functions::getReturnDateType(); + Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); + + // initialize colors array + $this->colors = []; + + // Initialise workbook writer + $this->writerWorkbook = new Xls\Workbook($this->spreadsheet, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser); + + // Initialise worksheet writers + $countSheets = $this->spreadsheet->getSheetCount(); + for ($i = 0; $i < $countSheets; ++$i) { + $this->writerWorksheets[$i] = new Xls\Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i)); + } + + // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook. + $this->buildWorksheetEschers(); + $this->buildWorkbookEscher(); + + // add 15 identical cell style Xfs + // for now, we use the first cellXf instead of cellStyleXf + $cellXfCollection = $this->spreadsheet->getCellXfCollection(); + for ($i = 0; $i < 15; ++$i) { + $this->writerWorkbook->addXfWriter($cellXfCollection[0], true); + } + + // add all the cell Xfs + foreach ($this->spreadsheet->getCellXfCollection() as $style) { + $this->writerWorkbook->addXfWriter($style, false); + } + + // add fonts from rich text eleemnts + for ($i = 0; $i < $countSheets; ++$i) { + foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) { + $cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate); + $cVal = $cell->getValue(); + if ($cVal instanceof RichText) { + $elements = $cVal->getRichTextElements(); + foreach ($elements as $element) { + if ($element instanceof Run) { + $font = $element->getFont(); + $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font); + } + } + } + } + } + + // initialize OLE file + $workbookStreamName = 'Workbook'; + $OLE = new File(OLE::ascToUcs($workbookStreamName)); + + // Write the worksheet streams before the global workbook stream, + // because the byte sizes of these are needed in the global workbook stream + $worksheetSizes = []; + for ($i = 0; $i < $countSheets; ++$i) { + $this->writerWorksheets[$i]->close(); + $worksheetSizes[] = $this->writerWorksheets[$i]->_datasize; + } + + // add binary data for global workbook stream + $OLE->append($this->writerWorkbook->writeWorkbook($worksheetSizes)); + + // add binary data for sheet streams + for ($i = 0; $i < $countSheets; ++$i) { + $OLE->append($this->writerWorksheets[$i]->getData()); + } + + $this->documentSummaryInformation = $this->writeDocumentSummaryInformation(); + // initialize OLE Document Summary Information + if (isset($this->documentSummaryInformation) && !empty($this->documentSummaryInformation)) { + $OLE_DocumentSummaryInformation = new File(OLE::ascToUcs(chr(5) . 'DocumentSummaryInformation')); + $OLE_DocumentSummaryInformation->append($this->documentSummaryInformation); + } + + $this->summaryInformation = $this->writeSummaryInformation(); + // initialize OLE Summary Information + if (isset($this->summaryInformation) && !empty($this->summaryInformation)) { + $OLE_SummaryInformation = new File(OLE::ascToUcs(chr(5) . 'SummaryInformation')); + $OLE_SummaryInformation->append($this->summaryInformation); + } + + // define OLE Parts + $arrRootData = [$OLE]; + // initialize OLE Properties file + if (isset($OLE_SummaryInformation)) { + $arrRootData[] = $OLE_SummaryInformation; + } + // initialize OLE Extended Properties file + if (isset($OLE_DocumentSummaryInformation)) { + $arrRootData[] = $OLE_DocumentSummaryInformation; + } + + $root = new Root(time(), time(), $arrRootData); + // save the OLE file + $this->openFileHandle($pFilename); + $root->save($this->fileHandle); + $this->maybeCloseFileHandle(); + + Functions::setReturnDateType($saveDateReturnType); + Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); + } + + /** + * Build the Worksheet Escher objects. + */ + private function buildWorksheetEschers(): void + { + // 1-based index to BstoreContainer + $blipIndex = 0; + $lastReducedSpId = 0; + $lastSpId = 0; + + foreach ($this->spreadsheet->getAllsheets() as $sheet) { + // sheet index + $sheetIndex = $sheet->getParent()->getIndex($sheet); + + $escher = null; + + // check if there are any shapes for this sheet + $filterRange = $sheet->getAutoFilter()->getRange(); + if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) { + continue; + } + + // create intermediate Escher object + $escher = new Escher(); + + // dgContainer + $dgContainer = new DgContainer(); + + // set the drawing index (we use sheet index + 1) + $dgId = $sheet->getParent()->getIndex($sheet) + 1; + $dgContainer->setDgId($dgId); + $escher->setDgContainer($dgContainer); + + // spgrContainer + $spgrContainer = new SpgrContainer(); + $dgContainer->setSpgrContainer($spgrContainer); + + // add one shape which is the group shape + $spContainer = new SpContainer(); + $spContainer->setSpgr(true); + $spContainer->setSpType(0); + $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10); + $spgrContainer->addChild($spContainer); + + // add the shapes + + $countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet + + foreach ($sheet->getDrawingCollection() as $drawing) { + ++$blipIndex; + + ++$countShapes[$sheetIndex]; + + // add the shape + $spContainer = new SpContainer(); + + // set the shape type + $spContainer->setSpType(0x004B); + // set the shape flag + $spContainer->setSpFlag(0x02); + + // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index) + $reducedSpId = $countShapes[$sheetIndex]; + $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10; + $spContainer->setSpId($spId); + + // keep track of last reducedSpId + $lastReducedSpId = $reducedSpId; + + // keep track of last spId + $lastSpId = $spId; + + // set the BLIP index + $spContainer->setOPT(0x4104, $blipIndex); + + // set coordinates and offsets, client anchor + $coordinates = $drawing->getCoordinates(); + $offsetX = $drawing->getOffsetX(); + $offsetY = $drawing->getOffsetY(); + $width = $drawing->getWidth(); + $height = $drawing->getHeight(); + + $twoAnchor = \PhpOffice\PhpSpreadsheet\Shared\Xls::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height); + + $spContainer->setStartCoordinates($twoAnchor['startCoordinates']); + $spContainer->setStartOffsetX($twoAnchor['startOffsetX']); + $spContainer->setStartOffsetY($twoAnchor['startOffsetY']); + $spContainer->setEndCoordinates($twoAnchor['endCoordinates']); + $spContainer->setEndOffsetX($twoAnchor['endOffsetX']); + $spContainer->setEndOffsetY($twoAnchor['endOffsetY']); + + $spgrContainer->addChild($spContainer); + } + + // AutoFilters + if (!empty($filterRange)) { + $rangeBounds = Coordinate::rangeBoundaries($filterRange); + $iNumColStart = $rangeBounds[0][0]; + $iNumColEnd = $rangeBounds[1][0]; + + $iInc = $iNumColStart; + while ($iInc <= $iNumColEnd) { + ++$countShapes[$sheetIndex]; + + // create an Drawing Object for the dropdown + $oDrawing = new BaseDrawing(); + // get the coordinates of drawing + $cDrawing = Coordinate::stringFromColumnIndex($iInc) . $rangeBounds[0][1]; + $oDrawing->setCoordinates($cDrawing); + $oDrawing->setWorksheet($sheet); + + // add the shape + $spContainer = new SpContainer(); + // set the shape type + $spContainer->setSpType(0x00C9); + // set the shape flag + $spContainer->setSpFlag(0x01); + + // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index) + $reducedSpId = $countShapes[$sheetIndex]; + $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10; + $spContainer->setSpId($spId); + + // keep track of last reducedSpId + $lastReducedSpId = $reducedSpId; + + // keep track of last spId + $lastSpId = $spId; + + $spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping + $spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape + $spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest + $spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash + $spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint + + // set coordinates and offsets, client anchor + $endCoordinates = Coordinate::stringFromColumnIndex($iInc); + $endCoordinates .= $rangeBounds[0][1] + 1; + + $spContainer->setStartCoordinates($cDrawing); + $spContainer->setStartOffsetX(0); + $spContainer->setStartOffsetY(0); + $spContainer->setEndCoordinates($endCoordinates); + $spContainer->setEndOffsetX(0); + $spContainer->setEndOffsetY(0); + + $spgrContainer->addChild($spContainer); + ++$iInc; + } + } + + // identifier clusters, used for workbook Escher object + $this->IDCLs[$dgId] = $lastReducedSpId; + + // set last shape index + $dgContainer->setLastSpId($lastSpId); + + // set the Escher object + $this->writerWorksheets[$sheetIndex]->setEscher($escher); + } + } + + private function processMemoryDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing, string $renderingFunctionx): void + { + switch ($renderingFunctionx) { + case MemoryDrawing::RENDERING_JPEG: + $blipType = BSE::BLIPTYPE_JPEG; + $renderingFunction = 'imagejpeg'; + + break; + default: + $blipType = BSE::BLIPTYPE_PNG; + $renderingFunction = 'imagepng'; + + break; + } + + ob_start(); + call_user_func($renderingFunction, $drawing->getImageResource()); + $blipData = ob_get_contents(); + ob_end_clean(); + + $blip = new Blip(); + $blip->setData($blipData); + + $BSE = new BSE(); + $BSE->setBlipType($blipType); + $BSE->setBlip($blip); + + $bstoreContainer->addBSE($BSE); + } + + private function processDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing): void + { + $blipData = ''; + $filename = $drawing->getPath(); + + [$imagesx, $imagesy, $imageFormat] = getimagesize($filename); + + switch ($imageFormat) { + case 1: // GIF, not supported by BIFF8, we convert to PNG + $blipType = BSE::BLIPTYPE_PNG; + ob_start(); + imagepng(imagecreatefromgif($filename)); + $blipData = ob_get_contents(); + ob_end_clean(); + + break; + case 2: // JPEG + $blipType = BSE::BLIPTYPE_JPEG; + $blipData = file_get_contents($filename); + + break; + case 3: // PNG + $blipType = BSE::BLIPTYPE_PNG; + $blipData = file_get_contents($filename); + + break; + case 6: // Windows DIB (BMP), we convert to PNG + $blipType = BSE::BLIPTYPE_PNG; + ob_start(); + imagepng(SharedDrawing::imagecreatefrombmp($filename)); + $blipData = ob_get_contents(); + ob_end_clean(); + + break; + } + if ($blipData) { + $blip = new Blip(); + $blip->setData($blipData); + + $BSE = new BSE(); + $BSE->setBlipType($blipType); + $BSE->setBlip($blip); + + $bstoreContainer->addBSE($BSE); + } + } + + private function processBaseDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing): void + { + if ($drawing instanceof Drawing) { + $this->processDrawing($bstoreContainer, $drawing); + } elseif ($drawing instanceof MemoryDrawing) { + $this->processMemoryDrawing($bstoreContainer, $drawing, $drawing->getRenderingFunction()); + } + } + + private function checkForDrawings(): bool + { + // any drawings in this workbook? + $found = false; + foreach ($this->spreadsheet->getAllSheets() as $sheet) { + if (count($sheet->getDrawingCollection()) > 0) { + $found = true; + + break; + } + } + + return $found; + } + + /** + * Build the Escher object corresponding to the MSODRAWINGGROUP record. + */ + private function buildWorkbookEscher(): void + { + // nothing to do if there are no drawings + if (!$this->checkForDrawings()) { + return; + } + + // if we reach here, then there are drawings in the workbook + $escher = new Escher(); + + // dggContainer + $dggContainer = new DggContainer(); + $escher->setDggContainer($dggContainer); + + // set IDCLs (identifier clusters) + $dggContainer->setIDCLs($this->IDCLs); + + // this loop is for determining maximum shape identifier of all drawing + $spIdMax = 0; + $totalCountShapes = 0; + $countDrawings = 0; + + foreach ($this->spreadsheet->getAllsheets() as $sheet) { + $sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet + + $addCount = 0; + foreach ($sheet->getDrawingCollection() as $drawing) { + $addCount = 1; + ++$sheetCountShapes; + ++$totalCountShapes; + + $spId = $sheetCountShapes | ($this->spreadsheet->getIndex($sheet) + 1) << 10; + $spIdMax = max($spId, $spIdMax); + } + $countDrawings += $addCount; + } + + $dggContainer->setSpIdMax($spIdMax + 1); + $dggContainer->setCDgSaved($countDrawings); + $dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing + + // bstoreContainer + $bstoreContainer = new BstoreContainer(); + $dggContainer->setBstoreContainer($bstoreContainer); + + // the BSE's (all the images) + foreach ($this->spreadsheet->getAllsheets() as $sheet) { + foreach ($sheet->getDrawingCollection() as $drawing) { + $this->processBaseDrawing($bstoreContainer, $drawing); + } + } + + // Set the Escher object + $this->writerWorkbook->setEscher($escher); + } + + /** + * Build the OLE Part for DocumentSummary Information. + * + * @return string + */ + private function writeDocumentSummaryInformation() + { + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + $data = pack('v', 0xFFFE); + // offset: 2; size: 2; + $data .= pack('v', 0x0000); + // offset: 4; size: 2; OS version + $data .= pack('v', 0x0106); + // offset: 6; size: 2; OS indicator + $data .= pack('v', 0x0002); + // offset: 8; size: 16 + $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00); + // offset: 24; size: 4; section count + $data .= pack('V', 0x0001); + + // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae + $data .= pack('vvvvvvvv', 0xD502, 0xD5CD, 0x2E9C, 0x101B, 0x9793, 0x0008, 0x2C2B, 0xAEF9); + // offset: 44; size: 4; offset of the start + $data .= pack('V', 0x30); + + // SECTION + $dataSection = []; + $dataSection_NumProps = 0; + $dataSection_Summary = ''; + $dataSection_Content = ''; + + // GKPIDDSI_CODEPAGE: CodePage + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x01], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer + 'data' => ['data' => 1252], + ]; + ++$dataSection_NumProps; + + // GKPIDDSI_CATEGORY : Category + $dataProp = $this->spreadsheet->getProperties()->getCategory(); + if ($dataProp) { + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x02], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x1E], + 'data' => ['data' => $dataProp, 'length' => strlen($dataProp)], + ]; + ++$dataSection_NumProps; + } + // GKPIDDSI_VERSION :Version of the application that wrote the property storage + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x17], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x03], + 'data' => ['pack' => 'V', 'data' => 0x000C0000], + ]; + ++$dataSection_NumProps; + // GKPIDDSI_SCALE : FALSE + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x0B], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x0B], + 'data' => ['data' => false], + ]; + ++$dataSection_NumProps; + // GKPIDDSI_LINKSDIRTY : True if any of the values for the linked properties have changed outside of the application + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x10], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x0B], + 'data' => ['data' => false], + ]; + ++$dataSection_NumProps; + // GKPIDDSI_SHAREDOC : FALSE + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x13], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x0B], + 'data' => ['data' => false], + ]; + ++$dataSection_NumProps; + // GKPIDDSI_HYPERLINKSCHANGED : True if any of the values for the _PID_LINKS (hyperlink text) have changed outside of the application + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x16], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x0B], + 'data' => ['data' => false], + ]; + ++$dataSection_NumProps; + + // GKPIDDSI_DOCSPARTS + // MS-OSHARED p75 (2.3.3.2.2.1) + // Structure is VtVecUnalignedLpstrValue (2.3.3.1.9) + // cElements + $dataProp = pack('v', 0x0001); + $dataProp .= pack('v', 0x0000); + // array of UnalignedLpstr + // cch + $dataProp .= pack('v', 0x000A); + $dataProp .= pack('v', 0x0000); + // value + $dataProp .= 'Worksheet' . chr(0); + + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x0D], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x101E], + 'data' => ['data' => $dataProp, 'length' => strlen($dataProp)], + ]; + ++$dataSection_NumProps; + + // GKPIDDSI_HEADINGPAIR + // VtVecHeadingPairValue + // cElements + $dataProp = pack('v', 0x0002); + $dataProp .= pack('v', 0x0000); + // Array of vtHeadingPair + // vtUnalignedString - headingString + // stringType + $dataProp .= pack('v', 0x001E); + // padding + $dataProp .= pack('v', 0x0000); + // UnalignedLpstr + // cch + $dataProp .= pack('v', 0x0013); + $dataProp .= pack('v', 0x0000); + // value + $dataProp .= 'Feuilles de calcul'; + // vtUnalignedString - headingParts + // wType : 0x0003 = 32 bit signed integer + $dataProp .= pack('v', 0x0300); + // padding + $dataProp .= pack('v', 0x0000); + // value + $dataProp .= pack('v', 0x0100); + $dataProp .= pack('v', 0x0000); + $dataProp .= pack('v', 0x0000); + $dataProp .= pack('v', 0x0000); + + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x0C], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x100C], + 'data' => ['data' => $dataProp, 'length' => strlen($dataProp)], + ]; + ++$dataSection_NumProps; + + // 4 Section Length + // 4 Property count + // 8 * $dataSection_NumProps (8 = ID (4) + OffSet(4)) + $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8; + foreach ($dataSection as $dataProp) { + // Summary + $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']); + // Offset + $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset); + // DataType + $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']); + // Data + if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer + $dataSection_Content .= pack('V', $dataProp['data']['data']); + + $dataSection_Content_Offset += 4 + 4; + } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer + $dataSection_Content .= pack('V', $dataProp['data']['data']); + + $dataSection_Content_Offset += 4 + 4; + } elseif ($dataProp['type']['data'] == 0x0B) { // Boolean + $dataSection_Content .= pack('V', (int) $dataProp['data']['data']); + $dataSection_Content_Offset += 4 + 4; + } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length + // Null-terminated string + $dataProp['data']['data'] .= chr(0); + ++$dataProp['data']['length']; + // Complete the string with null string for being a %4 + $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4)); + $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT); + + $dataSection_Content .= pack('V', $dataProp['data']['length']); + $dataSection_Content .= $dataProp['data']['data']; + + $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']); + // Condition below can never be true + //} elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + // $dataSection_Content .= $dataProp['data']['data']; + + // $dataSection_Content_Offset += 4 + 8; + } else { + $dataSection_Content .= $dataProp['data']['data']; + + $dataSection_Content_Offset += 4 + $dataProp['data']['length']; + } + } + // Now $dataSection_Content_Offset contains the size of the content + + // section header + // offset: $secOffset; size: 4; section length + // + x Size of the content (summary + content) + $data .= pack('V', $dataSection_Content_Offset); + // offset: $secOffset+4; size: 4; property count + $data .= pack('V', $dataSection_NumProps); + // Section Summary + $data .= $dataSection_Summary; + // Section Content + $data .= $dataSection_Content; + + return $data; + } + + private function writeSummaryPropOle(int $dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void + { + if ($dataProp) { + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => $sumdata], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => $typdata], // null-terminated string prepended by dword string length + 'data' => ['data' => OLE::localDateToOLE($dataProp)], + ]; + ++$dataSection_NumProps; + } + } + + private function writeSummaryProp(string $dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void + { + if ($dataProp) { + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => $sumdata], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => $typdata], // null-terminated string prepended by dword string length + 'data' => ['data' => $dataProp, 'length' => strlen($dataProp)], + ]; + ++$dataSection_NumProps; + } + } + + /** + * Build the OLE Part for Summary Information. + * + * @return string + */ + private function writeSummaryInformation() + { + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + $data = pack('v', 0xFFFE); + // offset: 2; size: 2; + $data .= pack('v', 0x0000); + // offset: 4; size: 2; OS version + $data .= pack('v', 0x0106); + // offset: 6; size: 2; OS indicator + $data .= pack('v', 0x0002); + // offset: 8; size: 16 + $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00); + // offset: 24; size: 4; section count + $data .= pack('V', 0x0001); + + // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 + $data .= pack('vvvvvvvv', 0x85E0, 0xF29F, 0x4FF9, 0x1068, 0x91AB, 0x0008, 0x272B, 0xD9B3); + // offset: 44; size: 4; offset of the start + $data .= pack('V', 0x30); + + // SECTION + $dataSection = []; + $dataSection_NumProps = 0; + $dataSection_Summary = ''; + $dataSection_Content = ''; + + // CodePage : CP-1252 + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x01], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x02], // 2 byte signed integer + 'data' => ['data' => 1252], + ]; + ++$dataSection_NumProps; + + $props = $this->spreadsheet->getProperties(); + $this->writeSummaryProp($props->getTitle(), $dataSection_NumProps, $dataSection, 0x02, 0x1e); + $this->writeSummaryProp($props->getSubject(), $dataSection_NumProps, $dataSection, 0x03, 0x1e); + $this->writeSummaryProp($props->getCreator(), $dataSection_NumProps, $dataSection, 0x04, 0x1e); + $this->writeSummaryProp($props->getKeywords(), $dataSection_NumProps, $dataSection, 0x05, 0x1e); + $this->writeSummaryProp($props->getDescription(), $dataSection_NumProps, $dataSection, 0x06, 0x1e); + $this->writeSummaryProp($props->getLastModifiedBy(), $dataSection_NumProps, $dataSection, 0x08, 0x1e); + $this->writeSummaryPropOle($props->getCreated(), $dataSection_NumProps, $dataSection, 0x0c, 0x40); + $this->writeSummaryPropOle($props->getModified(), $dataSection_NumProps, $dataSection, 0x0d, 0x40); + + // Security + $dataSection[] = [ + 'summary' => ['pack' => 'V', 'data' => 0x13], + 'offset' => ['pack' => 'V'], + 'type' => ['pack' => 'V', 'data' => 0x03], // 4 byte signed integer + 'data' => ['data' => 0x00], + ]; + ++$dataSection_NumProps; + + // 4 Section Length + // 4 Property count + // 8 * $dataSection_NumProps (8 = ID (4) + OffSet(4)) + $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8; + foreach ($dataSection as $dataProp) { + // Summary + $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']); + // Offset + $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset); + // DataType + $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']); + // Data + if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer + $dataSection_Content .= pack('V', $dataProp['data']['data']); + + $dataSection_Content_Offset += 4 + 4; + } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer + $dataSection_Content .= pack('V', $dataProp['data']['data']); + + $dataSection_Content_Offset += 4 + 4; + } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length + // Null-terminated string + $dataProp['data']['data'] .= chr(0); + ++$dataProp['data']['length']; + // Complete the string with null string for being a %4 + $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4) == 4 ? 0 : (4 - $dataProp['data']['length'] % 4)); + $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT); + + $dataSection_Content .= pack('V', $dataProp['data']['length']); + $dataSection_Content .= $dataProp['data']['data']; + + $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']); + } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + $dataSection_Content .= $dataProp['data']['data']; + + $dataSection_Content_Offset += 4 + 8; + } + // Data Type Not Used at the moment + } + // Now $dataSection_Content_Offset contains the size of the content + + // section header + // offset: $secOffset; size: 4; section length + // + x Size of the content (summary + content) + $data .= pack('V', $dataSection_Content_Offset); + // offset: $secOffset+4; size: 4; property count + $data .= pack('V', $dataSection_NumProps); + // Section Summary + $data .= $dataSection_Summary; + // Section Content + $data .= $dataSection_Content; + + return $data; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php new file mode 100644 index 00000000000..84e27d0d814 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php @@ -0,0 +1,224 @@ + +// * +// * The majority of this is _NOT_ my code. I simply ported it from the +// * PERL Spreadsheet::WriteExcel module. +// * +// * The author of the Spreadsheet::WriteExcel module is John McNamara +// * +// * +// * I _DO_ maintain this code, and John McNamara has nothing to do with the +// * porting of this code to PHP. Any questions directly related to this +// * class library should be directed to me. +// * +// * License Information: +// * +// * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +// * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +// * +// * This library is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +// * +// * This library is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public +// * License along with this library; if not, write to the Free Software +// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// */ +class BIFFwriter +{ + /** + * The byte order of this architecture. 0 => little endian, 1 => big endian. + * + * @var int + */ + private static $byteOrder; + + /** + * The string containing the data of the BIFF stream. + * + * @var string + */ + public $_data; + + /** + * The size of the data in bytes. Should be the same as strlen($this->_data). + * + * @var int + */ + public $_datasize; + + /** + * The maximum length for a BIFF record (excluding record header and length field). See addContinue(). + * + * @var int + * + * @see addContinue() + */ + private $limit = 8224; + + /** + * Constructor. + */ + public function __construct() + { + $this->_data = ''; + $this->_datasize = 0; + } + + /** + * Determine the byte order and store it as class data to avoid + * recalculating it for each call to new(). + * + * @return int + */ + public static function getByteOrder() + { + if (!isset(self::$byteOrder)) { + // Check if "pack" gives the required IEEE 64bit float + $teststr = pack('d', 1.2345); + $number = pack('C8', 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); + if ($number == $teststr) { + $byte_order = 0; // Little Endian + } elseif ($number == strrev($teststr)) { + $byte_order = 1; // Big Endian + } else { + // Give up. I'll fix this in a later version. + throw new WriterException('Required floating point format not supported on this platform.'); + } + self::$byteOrder = $byte_order; + } + + return self::$byteOrder; + } + + /** + * General storage function. + * + * @param string $data binary data to append + */ + protected function append($data): void + { + if (strlen($data) - 4 > $this->limit) { + $data = $this->addContinue($data); + } + $this->_data .= $data; + $this->_datasize += strlen($data); + } + + /** + * General storage function like append, but returns string instead of modifying $this->_data. + * + * @param string $data binary data to write + * + * @return string + */ + public function writeData($data) + { + if (strlen($data) - 4 > $this->limit) { + $data = $this->addContinue($data); + } + $this->_datasize += strlen($data); + + return $data; + } + + /** + * Writes Excel BOF record to indicate the beginning of a stream or + * sub-stream in the BIFF file. + * + * @param int $type type of BIFF file to write: 0x0005 Workbook, + * 0x0010 Worksheet + */ + protected function storeBof($type): void + { + $record = 0x0809; // Record identifier (BIFF5-BIFF8) + $length = 0x0010; + + // by inspection of real files, MS Office Excel 2007 writes the following + $unknown = pack('VV', 0x000100D1, 0x00000406); + + $build = 0x0DBB; // Excel 97 + $year = 0x07CC; // Excel 97 + + $version = 0x0600; // BIFF8 + + $header = pack('vv', $record, $length); + $data = pack('vvvv', $version, $type, $build, $year); + $this->append($header . $data . $unknown); + } + + /** + * Writes Excel EOF record to indicate the end of a BIFF stream. + */ + protected function storeEof(): void + { + $record = 0x000A; // Record identifier + $length = 0x0000; // Number of bytes to follow + + $header = pack('vv', $record, $length); + $this->append($header); + } + + /** + * Writes Excel EOF record to indicate the end of a BIFF stream. + */ + public function writeEof() + { + $record = 0x000A; // Record identifier + $length = 0x0000; // Number of bytes to follow + $header = pack('vv', $record, $length); + + return $this->writeData($header); + } + + /** + * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In + * Excel 97 the limit is 8228 bytes. Records that are longer than these limits + * must be split up into CONTINUE blocks. + * + * This function takes a long BIFF record and inserts CONTINUE records as + * necessary. + * + * @param string $data The original binary data to be written + * + * @return string A very convenient string of continue blocks + */ + private function addContinue($data) + { + $limit = $this->limit; + $record = 0x003C; // Record identifier + + // The first 2080/8224 bytes remain intact. However, we have to change + // the length field of the record. + $tmp = substr($data, 0, 2) . pack('v', $limit) . substr($data, 4, $limit); + + $header = pack('vv', $record, $limit); // Headers for continue records + + // Retrieve chunks of 2080/8224 bytes +4 for the header. + $data_length = strlen($data); + for ($i = $limit + 4; $i < ($data_length - $limit); $i += $limit) { + $tmp .= $header; + $tmp .= substr($data, $i, $limit); + } + + // Retrieve the last chunk of data + $header = pack('vv', $record, strlen($data) - $i); + $tmp .= $header; + $tmp .= substr($data, $i); + + return $tmp; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php new file mode 100644 index 00000000000..1ee2e904714 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php @@ -0,0 +1,510 @@ +object = $object; + } + + /** + * Process the object to be written. + * + * @return string + */ + public function close() + { + // initialize + $this->data = ''; + + switch (get_class($this->object)) { + case \PhpOffice\PhpSpreadsheet\Shared\Escher::class: + if ($dggContainer = $this->object->getDggContainer()) { + $writer = new self($dggContainer); + $this->data = $writer->close(); + } elseif ($dgContainer = $this->object->getDgContainer()) { + $writer = new self($dgContainer); + $this->data = $writer->close(); + $this->spOffsets = $writer->getSpOffsets(); + $this->spTypes = $writer->getSpTypes(); + } + + break; + case DggContainer::class: + // this is a container record + + // initialize + $innerData = ''; + + // write the dgg + $recVer = 0x0; + $recInstance = 0x0000; + $recType = 0xF006; + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + // dgg data + $dggData = + pack( + 'VVVV', + $this->object->getSpIdMax(), // maximum shape identifier increased by one + $this->object->getCDgSaved() + 1, // number of file identifier clusters increased by one + $this->object->getCSpSaved(), + $this->object->getCDgSaved() // count total number of drawings saved + ); + + // add file identifier clusters (one per drawing) + $IDCLs = $this->object->getIDCLs(); + + foreach ($IDCLs as $dgId => $maxReducedSpId) { + $dggData .= pack('VV', $dgId, $maxReducedSpId + 1); + } + + $header = pack('vvV', $recVerInstance, $recType, strlen($dggData)); + $innerData .= $header . $dggData; + + // write the bstoreContainer + if ($bstoreContainer = $this->object->getBstoreContainer()) { + $writer = new self($bstoreContainer); + $innerData .= $writer->close(); + } + + // write the record + $recVer = 0xF; + $recInstance = 0x0000; + $recType = 0xF000; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header . $innerData; + + break; + case BstoreContainer::class: + // this is a container record + + // initialize + $innerData = ''; + + // treat the inner data + if ($BSECollection = $this->object->getBSECollection()) { + foreach ($BSECollection as $BSE) { + $writer = new self($BSE); + $innerData .= $writer->close(); + } + } + + // write the record + $recVer = 0xF; + $recInstance = count($this->object->getBSECollection()); + $recType = 0xF001; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header . $innerData; + + break; + case BSE::class: + // this is a semi-container record + + // initialize + $innerData = ''; + + // here we treat the inner data + if ($blip = $this->object->getBlip()) { + $writer = new self($blip); + $innerData .= $writer->close(); + } + + // initialize + $data = ''; + + $btWin32 = $this->object->getBlipType(); + $btMacOS = $this->object->getBlipType(); + $data .= pack('CC', $btWin32, $btMacOS); + + $rgbUid = pack('VVVV', 0, 0, 0, 0); // todo + $data .= $rgbUid; + + $tag = 0; + $size = strlen($innerData); + $cRef = 1; + $foDelay = 0; //todo + $unused1 = 0x0; + $cbName = 0x0; + $unused2 = 0x0; + $unused3 = 0x0; + $data .= pack('vVVVCCCC', $tag, $size, $cRef, $foDelay, $unused1, $cbName, $unused2, $unused3); + + $data .= $innerData; + + // write the record + $recVer = 0x2; + $recInstance = $this->object->getBlipType(); + $recType = 0xF007; + $length = strlen($data); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header; + + $this->data .= $data; + + break; + case Blip::class: + // this is an atom record + + // write the record + switch ($this->object->getParent()->getBlipType()) { + case BSE::BLIPTYPE_JPEG: + // initialize + $innerData = ''; + + $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo + $innerData .= $rgbUid1; + + $tag = 0xFF; // todo + $innerData .= pack('C', $tag); + + $innerData .= $this->object->getData(); + + $recVer = 0x0; + $recInstance = 0x46A; + $recType = 0xF01D; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header; + + $this->data .= $innerData; + + break; + case BSE::BLIPTYPE_PNG: + // initialize + $innerData = ''; + + $rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo + $innerData .= $rgbUid1; + + $tag = 0xFF; // todo + $innerData .= pack('C', $tag); + + $innerData .= $this->object->getData(); + + $recVer = 0x0; + $recInstance = 0x6E0; + $recType = 0xF01E; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header; + + $this->data .= $innerData; + + break; + } + + break; + case DgContainer::class: + // this is a container record + + // initialize + $innerData = ''; + + // write the dg + $recVer = 0x0; + $recInstance = $this->object->getDgId(); + $recType = 0xF008; + $length = 8; + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + // number of shapes in this drawing (including group shape) + $countShapes = count($this->object->getSpgrContainer()->getChildren()); + $innerData .= $header . pack('VV', $countShapes, $this->object->getLastSpId()); + + // write the spgrContainer + if ($spgrContainer = $this->object->getSpgrContainer()) { + $writer = new self($spgrContainer); + $innerData .= $writer->close(); + + // get the shape offsets relative to the spgrContainer record + $spOffsets = $writer->getSpOffsets(); + $spTypes = $writer->getSpTypes(); + + // save the shape offsets relative to dgContainer + foreach ($spOffsets as &$spOffset) { + $spOffset += 24; // add length of dgContainer header data (8 bytes) plus dg data (16 bytes) + } + + $this->spOffsets = $spOffsets; + $this->spTypes = $spTypes; + } + + // write the record + $recVer = 0xF; + $recInstance = 0x0000; + $recType = 0xF002; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header . $innerData; + + break; + case SpgrContainer::class: + // this is a container record + + // initialize + $innerData = ''; + + // initialize spape offsets + $totalSize = 8; + $spOffsets = []; + $spTypes = []; + + // treat the inner data + foreach ($this->object->getChildren() as $spContainer) { + $writer = new self($spContainer); + $spData = $writer->close(); + $innerData .= $spData; + + // save the shape offsets (where new shape records begin) + $totalSize += strlen($spData); + $spOffsets[] = $totalSize; + + $spTypes = array_merge($spTypes, $writer->getSpTypes()); + } + + // write the record + $recVer = 0xF; + $recInstance = 0x0000; + $recType = 0xF003; + $length = strlen($innerData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header . $innerData; + $this->spOffsets = $spOffsets; + $this->spTypes = $spTypes; + + break; + case SpContainer::class: + // initialize + $data = ''; + + // build the data + + // write group shape record, if necessary? + if ($this->object->getSpgr()) { + $recVer = 0x1; + $recInstance = 0x0000; + $recType = 0xF009; + $length = 0x00000010; + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $data .= $header . pack('VVVV', 0, 0, 0, 0); + } + $this->spTypes[] = ($this->object->getSpType()); + + // write the shape record + $recVer = 0x2; + $recInstance = $this->object->getSpType(); // shape type + $recType = 0xF00A; + $length = 0x00000008; + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $data .= $header . pack('VV', $this->object->getSpId(), $this->object->getSpgr() ? 0x0005 : 0x0A00); + + // the options + if ($this->object->getOPTCollection()) { + $optData = ''; + + $recVer = 0x3; + $recInstance = count($this->object->getOPTCollection()); + $recType = 0xF00B; + foreach ($this->object->getOPTCollection() as $property => $value) { + $optData .= pack('vV', $property, $value); + } + $length = strlen($optData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + $data .= $header . $optData; + } + + // the client anchor + if ($this->object->getStartCoordinates()) { + $clientAnchorData = ''; + + $recVer = 0x0; + $recInstance = 0x0; + $recType = 0xF010; + + // start coordinates + [$column, $row] = Coordinate::coordinateFromString($this->object->getStartCoordinates()); + $c1 = Coordinate::columnIndexFromString($column) - 1; + $r1 = $row - 1; + + // start offsetX + $startOffsetX = $this->object->getStartOffsetX(); + + // start offsetY + $startOffsetY = $this->object->getStartOffsetY(); + + // end coordinates + [$column, $row] = Coordinate::coordinateFromString($this->object->getEndCoordinates()); + $c2 = Coordinate::columnIndexFromString($column) - 1; + $r2 = $row - 1; + + // end offsetX + $endOffsetX = $this->object->getEndOffsetX(); + + // end offsetY + $endOffsetY = $this->object->getEndOffsetY(); + + $clientAnchorData = pack('vvvvvvvvv', $this->object->getSpFlag(), $c1, $startOffsetX, $r1, $startOffsetY, $c2, $endOffsetX, $r2, $endOffsetY); + + $length = strlen($clientAnchorData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + $data .= $header . $clientAnchorData; + } + + // the client data, just empty for now + if (!$this->object->getSpgr()) { + $clientDataData = ''; + + $recVer = 0x0; + $recInstance = 0x0; + $recType = 0xF011; + + $length = strlen($clientDataData); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + $data .= $header . $clientDataData; + } + + // write the record + $recVer = 0xF; + $recInstance = 0x0000; + $recType = 0xF004; + $length = strlen($data); + + $recVerInstance = $recVer; + $recVerInstance |= $recInstance << 4; + + $header = pack('vvV', $recVerInstance, $recType, $length); + + $this->data = $header . $data; + + break; + } + + return $this->data; + } + + /** + * Gets the shape offsets. + * + * @return array + */ + public function getSpOffsets() + { + return $this->spOffsets; + } + + /** + * Gets the shape types. + * + * @return array + */ + public function getSpTypes() + { + return $this->spTypes; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php new file mode 100644 index 00000000000..9cb31ead085 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php @@ -0,0 +1,147 @@ +colorIndex = 0x7FFF; + $this->font = $font; + } + + /** + * Set the color index. + * + * @param int $colorIndex + */ + public function setColorIndex($colorIndex): void + { + $this->colorIndex = $colorIndex; + } + + /** + * Get font record data. + * + * @return string + */ + public function writeFont() + { + $font_outline = 0; + $font_shadow = 0; + + $icv = $this->colorIndex; // Index to color palette + if ($this->font->getSuperscript()) { + $sss = 1; + } elseif ($this->font->getSubscript()) { + $sss = 2; + } else { + $sss = 0; + } + $bFamily = 0; // Font family + $bCharSet = \PhpOffice\PhpSpreadsheet\Shared\Font::getCharsetFromFontName($this->font->getName()); // Character set + + $record = 0x31; // Record identifier + $reserved = 0x00; // Reserved + $grbit = 0x00; // Font attributes + if ($this->font->getItalic()) { + $grbit |= 0x02; + } + if ($this->font->getStrikethrough()) { + $grbit |= 0x08; + } + if ($font_outline) { + $grbit |= 0x10; + } + if ($font_shadow) { + $grbit |= 0x20; + } + + $data = pack( + 'vvvvvCCCC', + // Fontsize (in twips) + $this->font->getSize() * 20, + $grbit, + // Colour + $icv, + // Font weight + self::mapBold($this->font->getBold()), + // Superscript/Subscript + $sss, + self::mapUnderline($this->font->getUnderline()), + $bFamily, + $bCharSet, + $reserved + ); + $data .= StringHelper::UTF8toBIFF8UnicodeShort($this->font->getName()); + + $length = strlen($data); + $header = pack('vv', $record, $length); + + return $header . $data; + } + + /** + * Map to BIFF5-BIFF8 codes for bold. + * + * @param bool $bold + * + * @return int + */ + private static function mapBold($bold) + { + if ($bold) { + return 0x2BC; // 700 = Bold font weight + } + + return 0x190; // 400 = Normal font weight + } + + /** + * Map of BIFF2-BIFF8 codes for underline styles. + * + * @var array of int + */ + private static $mapUnderline = [ + \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE => 0x00, + \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE => 0x01, + \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE => 0x02, + \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING => 0x21, + \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING => 0x22, + ]; + + /** + * Map underline. + * + * @param string $underline + * + * @return int + */ + private static function mapUnderline($underline) + { + if (isset(self::$mapUnderline[$underline])) { + return self::$mapUnderline[$underline]; + } + + return 0x00; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php new file mode 100644 index 00000000000..f89957a442b --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php @@ -0,0 +1,1483 @@ +=,;#()"{} + const REGEX_SHEET_TITLE_UNQUOTED = '[^\*\:\/\\\\\?\[\]\+\-\% \\\'\^\&\<\>\=\,\;\#\(\)\"\{\}]+'; + + // Sheet title in quoted form (without surrounding quotes) + // Invalid sheet title characters cannot occur in the sheet title: + // *:/\?[] (usual invalid sheet title characters) + // Single quote is represented as a pair '' + const REGEX_SHEET_TITLE_QUOTED = '(([^\*\:\/\\\\\?\[\]\\\'])+|(\\\'\\\')+)+'; + + /** + * The index of the character we are currently looking at. + * + * @var int + */ + public $currentCharacter; + + /** + * The token we are working on. + * + * @var string + */ + public $currentToken; + + /** + * The formula to parse. + * + * @var string + */ + private $formula; + + /** + * The character ahead of the current char. + * + * @var string + */ + public $lookAhead; + + /** + * The parse tree to be generated. + * + * @var string + */ + public $parseTree; + + /** + * Array of external sheets. + * + * @var array + */ + private $externalSheets; + + /** + * Array of sheet references in the form of REF structures. + * + * @var array + */ + public $references; + + /** + * The Excel ptg indices. + * + * @var array + */ + private $ptg = [ + 'ptgExp' => 0x01, + 'ptgTbl' => 0x02, + 'ptgAdd' => 0x03, + 'ptgSub' => 0x04, + 'ptgMul' => 0x05, + 'ptgDiv' => 0x06, + 'ptgPower' => 0x07, + 'ptgConcat' => 0x08, + 'ptgLT' => 0x09, + 'ptgLE' => 0x0A, + 'ptgEQ' => 0x0B, + 'ptgGE' => 0x0C, + 'ptgGT' => 0x0D, + 'ptgNE' => 0x0E, + 'ptgIsect' => 0x0F, + 'ptgUnion' => 0x10, + 'ptgRange' => 0x11, + 'ptgUplus' => 0x12, + 'ptgUminus' => 0x13, + 'ptgPercent' => 0x14, + 'ptgParen' => 0x15, + 'ptgMissArg' => 0x16, + 'ptgStr' => 0x17, + 'ptgAttr' => 0x19, + 'ptgSheet' => 0x1A, + 'ptgEndSheet' => 0x1B, + 'ptgErr' => 0x1C, + 'ptgBool' => 0x1D, + 'ptgInt' => 0x1E, + 'ptgNum' => 0x1F, + 'ptgArray' => 0x20, + 'ptgFunc' => 0x21, + 'ptgFuncVar' => 0x22, + 'ptgName' => 0x23, + 'ptgRef' => 0x24, + 'ptgArea' => 0x25, + 'ptgMemArea' => 0x26, + 'ptgMemErr' => 0x27, + 'ptgMemNoMem' => 0x28, + 'ptgMemFunc' => 0x29, + 'ptgRefErr' => 0x2A, + 'ptgAreaErr' => 0x2B, + 'ptgRefN' => 0x2C, + 'ptgAreaN' => 0x2D, + 'ptgMemAreaN' => 0x2E, + 'ptgMemNoMemN' => 0x2F, + 'ptgNameX' => 0x39, + 'ptgRef3d' => 0x3A, + 'ptgArea3d' => 0x3B, + 'ptgRefErr3d' => 0x3C, + 'ptgAreaErr3d' => 0x3D, + 'ptgArrayV' => 0x40, + 'ptgFuncV' => 0x41, + 'ptgFuncVarV' => 0x42, + 'ptgNameV' => 0x43, + 'ptgRefV' => 0x44, + 'ptgAreaV' => 0x45, + 'ptgMemAreaV' => 0x46, + 'ptgMemErrV' => 0x47, + 'ptgMemNoMemV' => 0x48, + 'ptgMemFuncV' => 0x49, + 'ptgRefErrV' => 0x4A, + 'ptgAreaErrV' => 0x4B, + 'ptgRefNV' => 0x4C, + 'ptgAreaNV' => 0x4D, + 'ptgMemAreaNV' => 0x4E, + 'ptgMemNoMemNV' => 0x4F, + 'ptgFuncCEV' => 0x58, + 'ptgNameXV' => 0x59, + 'ptgRef3dV' => 0x5A, + 'ptgArea3dV' => 0x5B, + 'ptgRefErr3dV' => 0x5C, + 'ptgAreaErr3dV' => 0x5D, + 'ptgArrayA' => 0x60, + 'ptgFuncA' => 0x61, + 'ptgFuncVarA' => 0x62, + 'ptgNameA' => 0x63, + 'ptgRefA' => 0x64, + 'ptgAreaA' => 0x65, + 'ptgMemAreaA' => 0x66, + 'ptgMemErrA' => 0x67, + 'ptgMemNoMemA' => 0x68, + 'ptgMemFuncA' => 0x69, + 'ptgRefErrA' => 0x6A, + 'ptgAreaErrA' => 0x6B, + 'ptgRefNA' => 0x6C, + 'ptgAreaNA' => 0x6D, + 'ptgMemAreaNA' => 0x6E, + 'ptgMemNoMemNA' => 0x6F, + 'ptgFuncCEA' => 0x78, + 'ptgNameXA' => 0x79, + 'ptgRef3dA' => 0x7A, + 'ptgArea3dA' => 0x7B, + 'ptgRefErr3dA' => 0x7C, + 'ptgAreaErr3dA' => 0x7D, + ]; + + /** + * Thanks to Michael Meeks and Gnumeric for the initial arg values. + * + * The following hash was generated by "function_locale.pl" in the distro. + * Refer to function_locale.pl for non-English function names. + * + * The array elements are as follow: + * ptg: The Excel function ptg code. + * args: The number of arguments that the function takes: + * >=0 is a fixed number of arguments. + * -1 is a variable number of arguments. + * class: The reference, value or array class of the function args. + * vol: The function is volatile. + * + * @var array + */ + private $functions = [ + // function ptg args class vol + 'COUNT' => [0, -1, 0, 0], + 'IF' => [1, -1, 1, 0], + 'ISNA' => [2, 1, 1, 0], + 'ISERROR' => [3, 1, 1, 0], + 'SUM' => [4, -1, 0, 0], + 'AVERAGE' => [5, -1, 0, 0], + 'MIN' => [6, -1, 0, 0], + 'MAX' => [7, -1, 0, 0], + 'ROW' => [8, -1, 0, 0], + 'COLUMN' => [9, -1, 0, 0], + 'NA' => [10, 0, 0, 0], + 'NPV' => [11, -1, 1, 0], + 'STDEV' => [12, -1, 0, 0], + 'DOLLAR' => [13, -1, 1, 0], + 'FIXED' => [14, -1, 1, 0], + 'SIN' => [15, 1, 1, 0], + 'COS' => [16, 1, 1, 0], + 'TAN' => [17, 1, 1, 0], + 'ATAN' => [18, 1, 1, 0], + 'PI' => [19, 0, 1, 0], + 'SQRT' => [20, 1, 1, 0], + 'EXP' => [21, 1, 1, 0], + 'LN' => [22, 1, 1, 0], + 'LOG10' => [23, 1, 1, 0], + 'ABS' => [24, 1, 1, 0], + 'INT' => [25, 1, 1, 0], + 'SIGN' => [26, 1, 1, 0], + 'ROUND' => [27, 2, 1, 0], + 'LOOKUP' => [28, -1, 0, 0], + 'INDEX' => [29, -1, 0, 1], + 'REPT' => [30, 2, 1, 0], + 'MID' => [31, 3, 1, 0], + 'LEN' => [32, 1, 1, 0], + 'VALUE' => [33, 1, 1, 0], + 'TRUE' => [34, 0, 1, 0], + 'FALSE' => [35, 0, 1, 0], + 'AND' => [36, -1, 0, 0], + 'OR' => [37, -1, 0, 0], + 'NOT' => [38, 1, 1, 0], + 'MOD' => [39, 2, 1, 0], + 'DCOUNT' => [40, 3, 0, 0], + 'DSUM' => [41, 3, 0, 0], + 'DAVERAGE' => [42, 3, 0, 0], + 'DMIN' => [43, 3, 0, 0], + 'DMAX' => [44, 3, 0, 0], + 'DSTDEV' => [45, 3, 0, 0], + 'VAR' => [46, -1, 0, 0], + 'DVAR' => [47, 3, 0, 0], + 'TEXT' => [48, 2, 1, 0], + 'LINEST' => [49, -1, 0, 0], + 'TREND' => [50, -1, 0, 0], + 'LOGEST' => [51, -1, 0, 0], + 'GROWTH' => [52, -1, 0, 0], + 'PV' => [56, -1, 1, 0], + 'FV' => [57, -1, 1, 0], + 'NPER' => [58, -1, 1, 0], + 'PMT' => [59, -1, 1, 0], + 'RATE' => [60, -1, 1, 0], + 'MIRR' => [61, 3, 0, 0], + 'IRR' => [62, -1, 0, 0], + 'RAND' => [63, 0, 1, 1], + 'MATCH' => [64, -1, 0, 0], + 'DATE' => [65, 3, 1, 0], + 'TIME' => [66, 3, 1, 0], + 'DAY' => [67, 1, 1, 0], + 'MONTH' => [68, 1, 1, 0], + 'YEAR' => [69, 1, 1, 0], + 'WEEKDAY' => [70, -1, 1, 0], + 'HOUR' => [71, 1, 1, 0], + 'MINUTE' => [72, 1, 1, 0], + 'SECOND' => [73, 1, 1, 0], + 'NOW' => [74, 0, 1, 1], + 'AREAS' => [75, 1, 0, 1], + 'ROWS' => [76, 1, 0, 1], + 'COLUMNS' => [77, 1, 0, 1], + 'OFFSET' => [78, -1, 0, 1], + 'SEARCH' => [82, -1, 1, 0], + 'TRANSPOSE' => [83, 1, 1, 0], + 'TYPE' => [86, 1, 1, 0], + 'ATAN2' => [97, 2, 1, 0], + 'ASIN' => [98, 1, 1, 0], + 'ACOS' => [99, 1, 1, 0], + 'CHOOSE' => [100, -1, 1, 0], + 'HLOOKUP' => [101, -1, 0, 0], + 'VLOOKUP' => [102, -1, 0, 0], + 'ISREF' => [105, 1, 0, 0], + 'LOG' => [109, -1, 1, 0], + 'CHAR' => [111, 1, 1, 0], + 'LOWER' => [112, 1, 1, 0], + 'UPPER' => [113, 1, 1, 0], + 'PROPER' => [114, 1, 1, 0], + 'LEFT' => [115, -1, 1, 0], + 'RIGHT' => [116, -1, 1, 0], + 'EXACT' => [117, 2, 1, 0], + 'TRIM' => [118, 1, 1, 0], + 'REPLACE' => [119, 4, 1, 0], + 'SUBSTITUTE' => [120, -1, 1, 0], + 'CODE' => [121, 1, 1, 0], + 'FIND' => [124, -1, 1, 0], + 'CELL' => [125, -1, 0, 1], + 'ISERR' => [126, 1, 1, 0], + 'ISTEXT' => [127, 1, 1, 0], + 'ISNUMBER' => [128, 1, 1, 0], + 'ISBLANK' => [129, 1, 1, 0], + 'T' => [130, 1, 0, 0], + 'N' => [131, 1, 0, 0], + 'DATEVALUE' => [140, 1, 1, 0], + 'TIMEVALUE' => [141, 1, 1, 0], + 'SLN' => [142, 3, 1, 0], + 'SYD' => [143, 4, 1, 0], + 'DDB' => [144, -1, 1, 0], + 'INDIRECT' => [148, -1, 1, 1], + 'CALL' => [150, -1, 1, 0], + 'CLEAN' => [162, 1, 1, 0], + 'MDETERM' => [163, 1, 2, 0], + 'MINVERSE' => [164, 1, 2, 0], + 'MMULT' => [165, 2, 2, 0], + 'IPMT' => [167, -1, 1, 0], + 'PPMT' => [168, -1, 1, 0], + 'COUNTA' => [169, -1, 0, 0], + 'PRODUCT' => [183, -1, 0, 0], + 'FACT' => [184, 1, 1, 0], + 'DPRODUCT' => [189, 3, 0, 0], + 'ISNONTEXT' => [190, 1, 1, 0], + 'STDEVP' => [193, -1, 0, 0], + 'VARP' => [194, -1, 0, 0], + 'DSTDEVP' => [195, 3, 0, 0], + 'DVARP' => [196, 3, 0, 0], + 'TRUNC' => [197, -1, 1, 0], + 'ISLOGICAL' => [198, 1, 1, 0], + 'DCOUNTA' => [199, 3, 0, 0], + 'USDOLLAR' => [204, -1, 1, 0], + 'FINDB' => [205, -1, 1, 0], + 'SEARCHB' => [206, -1, 1, 0], + 'REPLACEB' => [207, 4, 1, 0], + 'LEFTB' => [208, -1, 1, 0], + 'RIGHTB' => [209, -1, 1, 0], + 'MIDB' => [210, 3, 1, 0], + 'LENB' => [211, 1, 1, 0], + 'ROUNDUP' => [212, 2, 1, 0], + 'ROUNDDOWN' => [213, 2, 1, 0], + 'ASC' => [214, 1, 1, 0], + 'DBCS' => [215, 1, 1, 0], + 'RANK' => [216, -1, 0, 0], + 'ADDRESS' => [219, -1, 1, 0], + 'DAYS360' => [220, -1, 1, 0], + 'TODAY' => [221, 0, 1, 1], + 'VDB' => [222, -1, 1, 0], + 'MEDIAN' => [227, -1, 0, 0], + 'SUMPRODUCT' => [228, -1, 2, 0], + 'SINH' => [229, 1, 1, 0], + 'COSH' => [230, 1, 1, 0], + 'TANH' => [231, 1, 1, 0], + 'ASINH' => [232, 1, 1, 0], + 'ACOSH' => [233, 1, 1, 0], + 'ATANH' => [234, 1, 1, 0], + 'DGET' => [235, 3, 0, 0], + 'INFO' => [244, 1, 1, 1], + 'DB' => [247, -1, 1, 0], + 'FREQUENCY' => [252, 2, 0, 0], + 'ERROR.TYPE' => [261, 1, 1, 0], + 'REGISTER.ID' => [267, -1, 1, 0], + 'AVEDEV' => [269, -1, 0, 0], + 'BETADIST' => [270, -1, 1, 0], + 'GAMMALN' => [271, 1, 1, 0], + 'BETAINV' => [272, -1, 1, 0], + 'BINOMDIST' => [273, 4, 1, 0], + 'CHIDIST' => [274, 2, 1, 0], + 'CHIINV' => [275, 2, 1, 0], + 'COMBIN' => [276, 2, 1, 0], + 'CONFIDENCE' => [277, 3, 1, 0], + 'CRITBINOM' => [278, 3, 1, 0], + 'EVEN' => [279, 1, 1, 0], + 'EXPONDIST' => [280, 3, 1, 0], + 'FDIST' => [281, 3, 1, 0], + 'FINV' => [282, 3, 1, 0], + 'FISHER' => [283, 1, 1, 0], + 'FISHERINV' => [284, 1, 1, 0], + 'FLOOR' => [285, 2, 1, 0], + 'GAMMADIST' => [286, 4, 1, 0], + 'GAMMAINV' => [287, 3, 1, 0], + 'CEILING' => [288, 2, 1, 0], + 'HYPGEOMDIST' => [289, 4, 1, 0], + 'LOGNORMDIST' => [290, 3, 1, 0], + 'LOGINV' => [291, 3, 1, 0], + 'NEGBINOMDIST' => [292, 3, 1, 0], + 'NORMDIST' => [293, 4, 1, 0], + 'NORMSDIST' => [294, 1, 1, 0], + 'NORMINV' => [295, 3, 1, 0], + 'NORMSINV' => [296, 1, 1, 0], + 'STANDARDIZE' => [297, 3, 1, 0], + 'ODD' => [298, 1, 1, 0], + 'PERMUT' => [299, 2, 1, 0], + 'POISSON' => [300, 3, 1, 0], + 'TDIST' => [301, 3, 1, 0], + 'WEIBULL' => [302, 4, 1, 0], + 'SUMXMY2' => [303, 2, 2, 0], + 'SUMX2MY2' => [304, 2, 2, 0], + 'SUMX2PY2' => [305, 2, 2, 0], + 'CHITEST' => [306, 2, 2, 0], + 'CORREL' => [307, 2, 2, 0], + 'COVAR' => [308, 2, 2, 0], + 'FORECAST' => [309, 3, 2, 0], + 'FTEST' => [310, 2, 2, 0], + 'INTERCEPT' => [311, 2, 2, 0], + 'PEARSON' => [312, 2, 2, 0], + 'RSQ' => [313, 2, 2, 0], + 'STEYX' => [314, 2, 2, 0], + 'SLOPE' => [315, 2, 2, 0], + 'TTEST' => [316, 4, 2, 0], + 'PROB' => [317, -1, 2, 0], + 'DEVSQ' => [318, -1, 0, 0], + 'GEOMEAN' => [319, -1, 0, 0], + 'HARMEAN' => [320, -1, 0, 0], + 'SUMSQ' => [321, -1, 0, 0], + 'KURT' => [322, -1, 0, 0], + 'SKEW' => [323, -1, 0, 0], + 'ZTEST' => [324, -1, 0, 0], + 'LARGE' => [325, 2, 0, 0], + 'SMALL' => [326, 2, 0, 0], + 'QUARTILE' => [327, 2, 0, 0], + 'PERCENTILE' => [328, 2, 0, 0], + 'PERCENTRANK' => [329, -1, 0, 0], + 'MODE' => [330, -1, 2, 0], + 'TRIMMEAN' => [331, 2, 0, 0], + 'TINV' => [332, 2, 1, 0], + 'CONCATENATE' => [336, -1, 1, 0], + 'POWER' => [337, 2, 1, 0], + 'RADIANS' => [342, 1, 1, 0], + 'DEGREES' => [343, 1, 1, 0], + 'SUBTOTAL' => [344, -1, 0, 0], + 'SUMIF' => [345, -1, 0, 0], + 'COUNTIF' => [346, 2, 0, 0], + 'COUNTBLANK' => [347, 1, 0, 0], + 'ISPMT' => [350, 4, 1, 0], + 'DATEDIF' => [351, 3, 1, 0], + 'DATESTRING' => [352, 1, 1, 0], + 'NUMBERSTRING' => [353, 2, 1, 0], + 'ROMAN' => [354, -1, 1, 0], + 'GETPIVOTDATA' => [358, -1, 0, 0], + 'HYPERLINK' => [359, -1, 1, 0], + 'PHONETIC' => [360, 1, 0, 0], + 'AVERAGEA' => [361, -1, 0, 0], + 'MAXA' => [362, -1, 0, 0], + 'MINA' => [363, -1, 0, 0], + 'STDEVPA' => [364, -1, 0, 0], + 'VARPA' => [365, -1, 0, 0], + 'STDEVA' => [366, -1, 0, 0], + 'VARA' => [367, -1, 0, 0], + 'BAHTTEXT' => [368, 1, 0, 0], + ]; + + private $spreadsheet; + + /** + * The class constructor. + */ + public function __construct(Spreadsheet $spreadsheet) + { + $this->spreadsheet = $spreadsheet; + + $this->currentCharacter = 0; + $this->currentToken = ''; // The token we are working on. + $this->formula = ''; // The formula to parse. + $this->lookAhead = ''; // The character ahead of the current char. + $this->parseTree = ''; // The parse tree to be generated. + $this->externalSheets = []; + $this->references = []; + } + + /** + * Convert a token to the proper ptg value. + * + * @param mixed $token the token to convert + * + * @return mixed the converted token on success + */ + private function convert($token) + { + if (preg_match('/"([^"]|""){0,255}"/', $token)) { + return $this->convertString($token); + } elseif (is_numeric($token)) { + return $this->convertNumber($token); + // match references like A1 or $A$1 + } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) { + return $this->convertRef2d($token); + // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1 + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) { + return $this->convertRef3d($token); + // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1 + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) { + return $this->convertRef3d($token); + // match ranges like A1:B2 or $A$1:$B$2 + } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) { + return $this->convertRange2d($token); + // match external ranges like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2 + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) { + return $this->convertRange3d($token); + // match external ranges like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2 + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) { + return $this->convertRange3d($token); + // operators (including parentheses) + } elseif (isset($this->ptg[$token])) { + return pack('C', $this->ptg[$token]); + // match error codes + } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') { + return $this->convertError($token); + } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) { + return $this->convertDefinedName($token); + // commented so argument number can be processed correctly. See toReversePolish(). + /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token)) + { + return($this->convertFunction($token, $this->_func_args)); + }*/ + // if it's an argument, ignore the token (the argument remains) + } elseif ($token == 'arg') { + return ''; + } + + // TODO: use real error codes + throw new WriterException("Unknown token $token"); + } + + /** + * Convert a number token to ptgInt or ptgNum. + * + * @param mixed $num an integer or double for conversion to its ptg value + * + * @return string + */ + private function convertNumber($num) + { + // Integer in the range 0..2**16-1 + if ((preg_match('/^\\d+$/', $num)) && ($num <= 65535)) { + return pack('Cv', $this->ptg['ptgInt'], $num); + } + + // A float + if (BIFFwriter::getByteOrder()) { // if it's Big Endian + $num = strrev($num); + } + + return pack('Cd', $this->ptg['ptgNum'], $num); + } + + /** + * Convert a string token to ptgStr. + * + * @param string $string a string for conversion to its ptg value + * + * @return mixed the converted token on success + */ + private function convertString($string) + { + // chop away beggining and ending quotes + $string = substr($string, 1, -1); + if (strlen($string) > 255) { + throw new WriterException('String is too long'); + } + + return pack('C', $this->ptg['ptgStr']) . StringHelper::UTF8toBIFF8UnicodeShort($string); + } + + /** + * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of + * args that it takes. + * + * @param string $token the name of the function for convertion to ptg value + * @param int $num_args the number of arguments the function receives + * + * @return string The packed ptg for the function + */ + private function convertFunction($token, $num_args) + { + $args = $this->functions[$token][1]; + + // Fixed number of args eg. TIME($i, $j, $k). + if ($args >= 0) { + return pack('Cv', $this->ptg['ptgFuncV'], $this->functions[$token][0]); + } + // Variable number of args eg. SUM($i, $j, $k, ..). + if ($args == -1) { + return pack('CCv', $this->ptg['ptgFuncVarV'], $num_args, $this->functions[$token][0]); + } + } + + /** + * Convert an Excel range such as A1:D4 to a ptgRefV. + * + * @param string $range An Excel range in the A1:A2 + * @param int $class + * + * @return string + */ + private function convertRange2d($range, $class = 0) + { + // TODO: possible class value 0,1,2 check Formula.pm + // Split the range into 2 cell refs + if (preg_match('/^(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)\:(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)$/', $range)) { + [$cell1, $cell2] = explode(':', $range); + } else { + // TODO: use real error codes + throw new WriterException('Unknown range separator'); + } + + // Convert the cell references + [$row1, $col1] = $this->cellToPackedRowcol($cell1); + [$row2, $col2] = $this->cellToPackedRowcol($cell2); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack('C', $this->ptg['ptgArea']); + } elseif ($class == 1) { + $ptgArea = pack('C', $this->ptg['ptgAreaV']); + } elseif ($class == 2) { + $ptgArea = pack('C', $this->ptg['ptgAreaA']); + } else { + // TODO: use real error codes + throw new WriterException("Unknown class $class"); + } + + return $ptgArea . $row1 . $row2 . $col1 . $col2; + } + + /** + * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to + * a ptgArea3d. + * + * @param string $token an Excel range in the Sheet1!A1:A2 format + * + * @return mixed the packed ptgArea3d token on success + */ + private function convertRange3d($token) + { + // Split the ref at the ! symbol + [$ext_ref, $range] = PhpspreadsheetWorksheet::extractSheetTitle($token, true); + + // Convert the external reference part (different for BIFF8) + $ext_ref = $this->getRefIndex($ext_ref); + + // Split the range into 2 cell refs + [$cell1, $cell2] = explode(':', $range); + + // Convert the cell references + if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\\d+)$/', $cell1)) { + [$row1, $col1] = $this->cellToPackedRowcol($cell1); + [$row2, $col2] = $this->cellToPackedRowcol($cell2); + } else { // It's a rows range (like 26:27) + [$row1, $col1, $row2, $col2] = $this->rangeToPackedRange($cell1 . ':' . $cell2); + } + + // The ptg value depends on the class of the ptg. + $ptgArea = pack('C', $this->ptg['ptgArea3d']); + + return $ptgArea . $ext_ref . $row1 . $row2 . $col1 . $col2; + } + + /** + * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. + * + * @param string $cell An Excel cell reference + * + * @return string The cell in packed() format with the corresponding ptg + */ + private function convertRef2d($cell) + { + // Convert the cell reference + $cell_array = $this->cellToPackedRowcol($cell); + [$row, $col] = $cell_array; + + // The ptg value depends on the class of the ptg. + $ptgRef = pack('C', $this->ptg['ptgRefA']); + + return $ptgRef . $row . $col; + } + + /** + * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a + * ptgRef3d. + * + * @param string $cell An Excel cell reference + * + * @return mixed the packed ptgRef3d token on success + */ + private function convertRef3d($cell) + { + // Split the ref at the ! symbol + [$ext_ref, $cell] = PhpspreadsheetWorksheet::extractSheetTitle($cell, true); + + // Convert the external reference part (different for BIFF8) + $ext_ref = $this->getRefIndex($ext_ref); + + // Convert the cell reference part + [$row, $col] = $this->cellToPackedRowcol($cell); + + // The ptg value depends on the class of the ptg. + $ptgRef = pack('C', $this->ptg['ptgRef3dA']); + + return $ptgRef . $ext_ref . $row . $col; + } + + /** + * Convert an error code to a ptgErr. + * + * @param string $errorCode The error code for conversion to its ptg value + * + * @return string The error code ptgErr + */ + private function convertError($errorCode) + { + switch ($errorCode) { + case '#NULL!': + return pack('C', 0x00); + case '#DIV/0!': + return pack('C', 0x07); + case '#VALUE!': + return pack('C', 0x0F); + case '#REF!': + return pack('C', 0x17); + case '#NAME?': + return pack('C', 0x1D); + case '#NUM!': + return pack('C', 0x24); + case '#N/A': + return pack('C', 0x2A); + } + + return pack('C', 0xFF); + } + + private function convertDefinedName(string $name): void + { + if (strlen($name) > 255) { + throw new WriterException('Defined Name is too long'); + } + + $nameReference = 1; + foreach ($this->spreadsheet->getDefinedNames() as $definedName) { + if ($name === $definedName->getName()) { + break; + } + ++$nameReference; + } + + $ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference); + + throw new WriterException('Cannot yet write formulae with defined names to Xls'); +// return $ptgRef; + } + + /** + * Look up the REF index that corresponds to an external sheet name + * (or range). If it doesn't exist yet add it to the workbook's references + * array. It assumes all sheet names given must exist. + * + * @param string $ext_ref The name of the external reference + * + * @return mixed The reference index in packed() format on success + */ + private function getRefIndex($ext_ref) + { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + $ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with ' + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match('/:/', $ext_ref)) { + [$sheet_name1, $sheet_name2] = explode(':', $ext_ref); + + $sheet1 = $this->getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + throw new WriterException("Unknown sheet name $sheet_name1 in formula"); + } + $sheet2 = $this->getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + throw new WriterException("Unknown sheet name $sheet_name2 in formula"); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + [$sheet1, $sheet2] = [$sheet2, $sheet1]; + } + } else { // Single sheet name only. + $sheet1 = $this->getSheetIndex($ext_ref); + if ($sheet1 == -1) { + throw new WriterException("Unknown sheet name $ext_ref in formula"); + } + $sheet2 = $sheet1; + } + + // assume all references belong to this document + $supbook_index = 0x00; + $ref = pack('vvv', $supbook_index, $sheet1, $sheet2); + $totalreferences = count($this->references); + $index = -1; + for ($i = 0; $i < $totalreferences; ++$i) { + if ($ref == $this->references[$i]) { + $index = $i; + + break; + } + } + // if REF was not found add it to references array + if ($index == -1) { + $this->references[$totalreferences] = $ref; + $index = $totalreferences; + } + + return pack('v', $index); + } + + /** + * Look up the index that corresponds to an external sheet name. The hash of + * sheet names is updated by the addworksheet() method of the + * \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook class. + * + * @param string $sheet_name Sheet name + * + * @return int The sheet index, -1 if the sheet was not found + */ + private function getSheetIndex($sheet_name) + { + if (!isset($this->externalSheets[$sheet_name])) { + return -1; + } + + return $this->externalSheets[$sheet_name]; + } + + /** + * This method is used to update the array of sheet names. It is + * called by the addWorksheet() method of the + * \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook class. + * + * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::addWorksheet() + * + * @param string $name The name of the worksheet being added + * @param int $index The index of the worksheet being added + */ + public function setExtSheet($name, $index): void + { + $this->externalSheets[$name] = $index; + } + + /** + * pack() row and column into the required 3 or 4 byte format. + * + * @param string $cell The Excel cell reference to be packed + * + * @return array Array containing the row and column in packed() format + */ + private function cellToPackedRowcol($cell) + { + $cell = strtoupper($cell); + [$row, $col, $row_rel, $col_rel] = $this->cellToRowcol($cell); + if ($col >= 256) { + throw new WriterException("Column in: $cell greater than 255"); + } + if ($row >= 65536) { + throw new WriterException("Row in: $cell greater than 65536 "); + } + + // Set the high bits to indicate if row or col are relative. + $col |= $col_rel << 14; + $col |= $row_rel << 15; + $col = pack('v', $col); + + $row = pack('v', $row); + + return [$row, $col]; + } + + /** + * pack() row range into the required 3 or 4 byte format. + * Just using maximum col/rows, which is probably not the correct solution. + * + * @param string $range The Excel range to be packed + * + * @return array Array containing (row1,col1,row2,col2) in packed() format + */ + private function rangeToPackedRange($range) + { + preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match); + // return absolute rows if there is a $ in the ref + $row1_rel = empty($match[1]) ? 1 : 0; + $row1 = $match[2]; + $row2_rel = empty($match[3]) ? 1 : 0; + $row2 = $match[4]; + // Convert 1-index to zero-index + --$row1; + --$row2; + // Trick poor inocent Excel + $col1 = 0; + $col2 = 65535; // FIXME: maximum possible value for Excel 5 (change this!!!) + + // FIXME: this changes for BIFF8 + if (($row1 >= 65536) || ($row2 >= 65536)) { + throw new WriterException("Row in: $range greater than 65536 "); + } + + // Set the high bits to indicate if rows are relative. + $col1 |= $row1_rel << 15; + $col2 |= $row2_rel << 15; + $col1 = pack('v', $col1); + $col2 = pack('v', $col2); + + $row1 = pack('v', $row1); + $row2 = pack('v', $row2); + + return [$row1, $col1, $row2, $col2]; + } + + /** + * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero + * indexed row and column number. Also returns two (0,1) values to indicate + * whether the row or column are relative references. + * + * @param string $cell the Excel cell reference in A1 format + * + * @return array + */ + private function cellToRowcol($cell) + { + preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/', $cell, $match); + // return absolute column if there is a $ in the ref + $col_rel = empty($match[1]) ? 1 : 0; + $col_ref = $match[2]; + $row_rel = empty($match[3]) ? 1 : 0; + $row = $match[4]; + + // Convert base26 column string to a number. + $expn = strlen($col_ref) - 1; + $col = 0; + $col_ref_length = strlen($col_ref); + for ($i = 0; $i < $col_ref_length; ++$i) { + $col += (ord($col_ref[$i]) - 64) * 26 ** $expn; + --$expn; + } + + // Convert 1-index to zero-index + --$row; + --$col; + + return [$row, $col, $row_rel, $col_rel]; + } + + /** + * Advance to the next valid token. + */ + private function advance() + { + $i = $this->currentCharacter; + $formula_length = strlen($this->formula); + // eat up white spaces + if ($i < $formula_length) { + while ($this->formula[$i] == ' ') { + ++$i; + } + + if ($i < ($formula_length - 1)) { + $this->lookAhead = $this->formula[$i + 1]; + } + $token = ''; + } + + while ($i < $formula_length) { + $token .= $this->formula[$i]; + + if ($i < ($formula_length - 1)) { + $this->lookAhead = $this->formula[$i + 1]; + } else { + $this->lookAhead = ''; + } + + if ($this->match($token) != '') { + $this->currentCharacter = $i + 1; + $this->currentToken = $token; + + return 1; + } + + if ($i < ($formula_length - 2)) { + $this->lookAhead = $this->formula[$i + 2]; + } else { // if we run out of characters lookAhead becomes empty + $this->lookAhead = ''; + } + ++$i; + } + //die("Lexical error ".$this->currentCharacter); + } + + /** + * Checks if it's a valid token. + * + * @param mixed $token the token to check + * + * @return mixed The checked token or false on failure + */ + private function match($token) + { + switch ($token) { + case '+': + case '-': + case '*': + case '/': + case '(': + case ')': + case ',': + case ';': + case '>=': + case '<=': + case '=': + case '<>': + case '^': + case '&': + case '%': + return $token; + + break; + case '>': + if ($this->lookAhead === '=') { // it's a GE token + break; + } + + return $token; + + break; + case '<': + // it's a LE or a NE token + if (($this->lookAhead === '=') || ($this->lookAhead === '>')) { + break; + } + + return $token; + + break; + default: + // if it's a reference A1 or $A$1 or $A1 or A$1 + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) { + return $token; + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) { + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1) + return $token; + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) { + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1) + return $token; + } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) { + // if it's a range A1:A2 or $A$1:$A$2 + return $token; + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) { + // If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2 + return $token; + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) { + // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2 + return $token; + } elseif (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) { + // If it's a number (check that it's not a sheet name or range) + return $token; + } elseif (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) { + // If it's a string (of maximum 255 characters) + return $token; + } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') { + // If it's an error code + return $token; + } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) { + // if it's a function call + return $token; + } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token) && $this->spreadsheet->getDefinedName($token) !== null) { + return $token; + } elseif (substr($token, -1) === ')') { + // It's an argument of some description (e.g. a named range), + // precise nature yet to be determined + return $token; + } + + return ''; + } + } + + /** + * The parsing method. It parses a formula. + * + * @param string $formula the formula to parse, without the initial equal + * sign (=) + * + * @return mixed true on success + */ + public function parse($formula) + { + $this->currentCharacter = 0; + $this->formula = (string) $formula; + $this->lookAhead = $formula[1] ?? ''; + $this->advance(); + $this->parseTree = $this->condition(); + + return true; + } + + /** + * It parses a condition. It assumes the following rule: + * Cond -> Expr [(">" | "<") Expr]. + * + * @return mixed The parsed ptg'd tree on success + */ + private function condition() + { + $result = $this->expression(); + if ($this->currentToken == '<') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgLT', $result, $result2); + } elseif ($this->currentToken == '>') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgGT', $result, $result2); + } elseif ($this->currentToken == '<=') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgLE', $result, $result2); + } elseif ($this->currentToken == '>=') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgGE', $result, $result2); + } elseif ($this->currentToken == '=') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgEQ', $result, $result2); + } elseif ($this->currentToken == '<>') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgNE', $result, $result2); + } elseif ($this->currentToken == '&') { + $this->advance(); + $result2 = $this->expression(); + $result = $this->createTree('ptgConcat', $result, $result2); + } + + return $result; + } + + /** + * It parses a expression. It assumes the following rule: + * Expr -> Term [("+" | "-") Term] + * -> "string" + * -> "-" Term : Negative value + * -> "+" Term : Positive value + * -> Error code. + * + * @return mixed The parsed ptg'd tree on success + */ + private function expression() + { + // If it's a string return a string node + if (preg_match('/"([^"]|""){0,255}"/', $this->currentToken)) { + $tmp = str_replace('""', '"', $this->currentToken); + if (($tmp == '"') || ($tmp == '')) { + // Trap for "" that has been used for an empty string + $tmp = '""'; + } + $result = $this->createTree($tmp, '', ''); + $this->advance(); + + return $result; + // If it's an error code + } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $this->currentToken) || $this->currentToken == '#N/A') { + $result = $this->createTree($this->currentToken, 'ptgErr', ''); + $this->advance(); + + return $result; + // If it's a negative value + } elseif ($this->currentToken == '-') { + // catch "-" Term + $this->advance(); + $result2 = $this->expression(); + + return $this->createTree('ptgUminus', $result2, ''); + // If it's a positive value + } elseif ($this->currentToken == '+') { + // catch "+" Term + $this->advance(); + $result2 = $this->expression(); + + return $this->createTree('ptgUplus', $result2, ''); + } + $result = $this->term(); + while ( + ($this->currentToken == '+') || + ($this->currentToken == '-') || + ($this->currentToken == '^') + ) { + if ($this->currentToken == '+') { + $this->advance(); + $result2 = $this->term(); + $result = $this->createTree('ptgAdd', $result, $result2); + } elseif ($this->currentToken == '-') { + $this->advance(); + $result2 = $this->term(); + $result = $this->createTree('ptgSub', $result, $result2); + } else { + $this->advance(); + $result2 = $this->term(); + $result = $this->createTree('ptgPower', $result, $result2); + } + } + + return $result; + } + + /** + * This function just introduces a ptgParen element in the tree, so that Excel + * doesn't get confused when working with a parenthesized formula afterwards. + * + * @see fact() + * + * @return array The parsed ptg'd tree + */ + private function parenthesizedExpression() + { + return $this->createTree('ptgParen', $this->expression(), ''); + } + + /** + * It parses a term. It assumes the following rule: + * Term -> Fact [("*" | "/") Fact]. + * + * @return mixed The parsed ptg'd tree on success + */ + private function term() + { + $result = $this->fact(); + while ( + ($this->currentToken == '*') || + ($this->currentToken == '/') + ) { + if ($this->currentToken == '*') { + $this->advance(); + $result2 = $this->fact(); + $result = $this->createTree('ptgMul', $result, $result2); + } else { + $this->advance(); + $result2 = $this->fact(); + $result = $this->createTree('ptgDiv', $result, $result2); + } + } + + return $result; + } + + /** + * It parses a factor. It assumes the following rule: + * Fact -> ( Expr ) + * | CellRef + * | CellRange + * | Number + * | Function. + * + * @return mixed The parsed ptg'd tree on success + */ + private function fact() + { + if ($this->currentToken === '(') { + $this->advance(); // eat the "(" + $result = $this->parenthesizedExpression(); + if ($this->currentToken !== ')') { + throw new WriterException("')' token expected."); + } + $this->advance(); // eat the ")" + + return $result; + } + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $this->currentToken)) { + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) { + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1) + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) { + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1) + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif ( + preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken) || + preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken) + ) { + // if it's a range A1:B2 or $A$1:$B$2 + // must be an error? + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) { + // If it's an external range (Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2) + // must be an error? + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) { + // If it's an external range ('Sheet1'!A1:B2 or 'Sheet1'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1'!$A$1:$B$2) + // must be an error? + $result = $this->createTree($this->currentToken, '', ''); + $this->advance(); + + return $result; + } elseif (is_numeric($this->currentToken)) { + // If it's a number or a percent + if ($this->lookAhead === '%') { + $result = $this->createTree('ptgPercent', $this->currentToken, ''); + $this->advance(); // Skip the percentage operator once we've pre-built that tree + } else { + $result = $this->createTree($this->currentToken, '', ''); + } + $this->advance(); + + return $result; + } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken) && ($this->lookAhead === '(')) { + // if it's a function call + return $this->func(); + } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $this->currentToken) && $this->spreadsheet->getDefinedName($this->currentToken) !== null) { + $result = $this->createTree('ptgName', $this->currentToken, ''); + $this->advance(); + + return $result; + } + + throw new WriterException('Syntax error: ' . $this->currentToken . ', lookahead: ' . $this->lookAhead . ', current char: ' . $this->currentCharacter); + } + + /** + * It parses a function call. It assumes the following rule: + * Func -> ( Expr [,Expr]* ). + * + * @return mixed The parsed ptg'd tree on success + */ + private function func() + { + $num_args = 0; // number of arguments received + $function = strtoupper($this->currentToken); + $result = ''; // initialize result + $this->advance(); + $this->advance(); // eat the "(" + while ($this->currentToken !== ')') { + if ($num_args > 0) { + if ($this->currentToken === ',' || $this->currentToken === ';') { + $this->advance(); // eat the "," or ";" + } else { + throw new WriterException("Syntax error: comma expected in function $function, arg #{$num_args}"); + } + $result2 = $this->condition(); + $result = $this->createTree('arg', $result, $result2); + } else { // first argument + $result2 = $this->condition(); + $result = $this->createTree('arg', '', $result2); + } + ++$num_args; + } + if (!isset($this->functions[$function])) { + throw new WriterException("Function $function() doesn't exist"); + } + $args = $this->functions[$function][1]; + // If fixed number of args eg. TIME($i, $j, $k). Check that the number of args is valid. + if (($args >= 0) && ($args != $num_args)) { + throw new WriterException("Incorrect number of arguments in function $function() "); + } + + $result = $this->createTree($function, $result, $num_args); + $this->advance(); // eat the ")" + + return $result; + } + + /** + * Creates a tree. In fact an array which may have one or two arrays (sub-trees) + * as elements. + * + * @param mixed $value the value of this node + * @param mixed $left the left array (sub-tree) or a final node + * @param mixed $right the right array (sub-tree) or a final node + * + * @return array A tree + */ + private function createTree($value, $left, $right) + { + return ['value' => $value, 'left' => $left, 'right' => $right]; + } + + /** + * Builds a string containing the tree in reverse polish notation (What you + * would use in a HP calculator stack). + * The following tree:. + * + * + + * / \ + * 2 3 + * + * produces: "23+" + * + * The following tree: + * + * + + * / \ + * 3 * + * / \ + * 6 A1 + * + * produces: "36A1*+" + * + * In fact all operands, functions, references, etc... are written as ptg's + * + * @param array $tree the optional tree to convert + * + * @return string The tree in reverse polish notation + */ + public function toReversePolish($tree = []) + { + $polish = ''; // the string we are going to return + if (empty($tree)) { // If it's the first call use parseTree + $tree = $this->parseTree; + } + + if (is_array($tree['left'])) { + $converted_tree = $this->toReversePolish($tree['left']); + $polish .= $converted_tree; + } elseif ($tree['left'] != '') { // It's a final node + $converted_tree = $this->convert($tree['left']); + $polish .= $converted_tree; + } + if (is_array($tree['right'])) { + $converted_tree = $this->toReversePolish($tree['right']); + $polish .= $converted_tree; + } elseif ($tree['right'] != '') { // It's a final node + $converted_tree = $this->convert($tree['right']); + $polish .= $converted_tree; + } + // if it's a function convert it here (so we can set it's arguments) + if ( + preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/", $tree['value']) && + !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/', $tree['value']) && + !preg_match('/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)$/', $tree['value']) && + !is_numeric($tree['value']) && + !isset($this->ptg[$tree['value']]) + ) { + // left subtree for a function is always an array. + if ($tree['left'] != '') { + $left_tree = $this->toReversePolish($tree['left']); + } else { + $left_tree = ''; + } + // add it's left subtree and return. + return $left_tree . $this->convertFunction($tree['value'], $tree['right']); + } + $converted_tree = $this->convert($tree['value']); + + return $polish . $converted_tree; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php new file mode 100644 index 00000000000..f752bce62de --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php @@ -0,0 +1,1191 @@ + +// * +// * The majority of this is _NOT_ my code. I simply ported it from the +// * PERL Spreadsheet::WriteExcel module. +// * +// * The author of the Spreadsheet::WriteExcel module is John McNamara +// * +// * +// * I _DO_ maintain this code, and John McNamara has nothing to do with the +// * porting of this code to PHP. Any questions directly related to this +// * class library should be directed to me. +// * +// * License Information: +// * +// * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +// * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +// * +// * This library is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +// * +// * This library is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public +// * License along with this library; if not, write to the Free Software +// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// */ +class Workbook extends BIFFwriter +{ + /** + * Formula parser. + * + * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser + */ + private $parser; + + /** + * The BIFF file size for the workbook. + * + * @var int + * + * @see calcSheetOffsets() + */ + private $biffSize; + + /** + * XF Writers. + * + * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf[] + */ + private $xfWriters = []; + + /** + * Array containing the colour palette. + * + * @var array + */ + private $palette; + + /** + * The codepage indicates the text encoding used for strings. + * + * @var int + */ + private $codepage; + + /** + * The country code used for localization. + * + * @var int + */ + private $countryCode; + + /** + * Workbook. + * + * @var Spreadsheet + */ + private $spreadsheet; + + /** + * Fonts writers. + * + * @var Font[] + */ + private $fontWriters = []; + + /** + * Added fonts. Maps from font's hash => index in workbook. + * + * @var array + */ + private $addedFonts = []; + + /** + * Shared number formats. + * + * @var array + */ + private $numberFormats = []; + + /** + * Added number formats. Maps from numberFormat's hash => index in workbook. + * + * @var array + */ + private $addedNumberFormats = []; + + /** + * Sizes of the binary worksheet streams. + * + * @var array + */ + private $worksheetSizes = []; + + /** + * Offsets of the binary worksheet streams relative to the start of the global workbook stream. + * + * @var array + */ + private $worksheetOffsets = []; + + /** + * Total number of shared strings in workbook. + * + * @var int + */ + private $stringTotal; + + /** + * Number of unique shared strings in workbook. + * + * @var int + */ + private $stringUnique; + + /** + * Array of unique shared strings in workbook. + * + * @var array + */ + private $stringTable; + + /** + * Color cache. + */ + private $colors; + + /** + * Escher object corresponding to MSODRAWINGGROUP. + * + * @var \PhpOffice\PhpSpreadsheet\Shared\Escher + */ + private $escher; + + /** + * Class constructor. + * + * @param Spreadsheet $spreadsheet The Workbook + * @param int $str_total Total number of strings + * @param int $str_unique Total number of unique strings + * @param array $str_table String Table + * @param array $colors Colour Table + * @param Parser $parser The formula parser created for the Workbook + */ + public function __construct(Spreadsheet $spreadsheet, &$str_total, &$str_unique, &$str_table, &$colors, Parser $parser) + { + // It needs to call its parent's constructor explicitly + parent::__construct(); + + $this->parser = $parser; + $this->biffSize = 0; + $this->palette = []; + $this->countryCode = -1; + + $this->stringTotal = &$str_total; + $this->stringUnique = &$str_unique; + $this->stringTable = &$str_table; + $this->colors = &$colors; + $this->setPaletteXl97(); + + $this->spreadsheet = $spreadsheet; + + $this->codepage = 0x04B0; + + // Add empty sheets and Build color cache + $countSheets = $spreadsheet->getSheetCount(); + for ($i = 0; $i < $countSheets; ++$i) { + $phpSheet = $spreadsheet->getSheet($i); + + $this->parser->setExtSheet($phpSheet->getTitle(), $i); // Register worksheet name with parser + + $supbook_index = 0x00; + $ref = pack('vvv', $supbook_index, $i, $i); + $this->parser->references[] = $ref; // Register reference with parser + + // Sheet tab colors? + if ($phpSheet->isTabColorSet()) { + $this->addColor($phpSheet->getTabColor()->getRGB()); + } + } + } + + /** + * Add a new XF writer. + * + * @param bool $isStyleXf Is it a style XF? + * + * @return int Index to XF record + */ + public function addXfWriter(Style $style, $isStyleXf = false) + { + $xfWriter = new Xf($style); + $xfWriter->setIsStyleXf($isStyleXf); + + // Add the font if not already added + $fontIndex = $this->addFont($style->getFont()); + + // Assign the font index to the xf record + $xfWriter->setFontIndex($fontIndex); + + // Background colors, best to treat these after the font so black will come after white in custom palette + $xfWriter->setFgColor($this->addColor($style->getFill()->getStartColor()->getRGB())); + $xfWriter->setBgColor($this->addColor($style->getFill()->getEndColor()->getRGB())); + $xfWriter->setBottomColor($this->addColor($style->getBorders()->getBottom()->getColor()->getRGB())); + $xfWriter->setTopColor($this->addColor($style->getBorders()->getTop()->getColor()->getRGB())); + $xfWriter->setRightColor($this->addColor($style->getBorders()->getRight()->getColor()->getRGB())); + $xfWriter->setLeftColor($this->addColor($style->getBorders()->getLeft()->getColor()->getRGB())); + $xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB())); + + // Add the number format if it is not a built-in one and not already added + if ($style->getNumberFormat()->getBuiltInFormatCode() === false) { + $numberFormatHashCode = $style->getNumberFormat()->getHashCode(); + + if (isset($this->addedNumberFormats[$numberFormatHashCode])) { + $numberFormatIndex = $this->addedNumberFormats[$numberFormatHashCode]; + } else { + $numberFormatIndex = 164 + count($this->numberFormats); + $this->numberFormats[$numberFormatIndex] = $style->getNumberFormat(); + $this->addedNumberFormats[$numberFormatHashCode] = $numberFormatIndex; + } + } else { + $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode(); + } + + // Assign the number format index to xf record + $xfWriter->setNumberFormatIndex($numberFormatIndex); + + $this->xfWriters[] = $xfWriter; + + return count($this->xfWriters) - 1; + } + + /** + * Add a font to added fonts. + * + * @return int Index to FONT record + */ + public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font) + { + $fontHashCode = $font->getHashCode(); + if (isset($this->addedFonts[$fontHashCode])) { + $fontIndex = $this->addedFonts[$fontHashCode]; + } else { + $countFonts = count($this->fontWriters); + $fontIndex = ($countFonts < 4) ? $countFonts : $countFonts + 1; + + $fontWriter = new Font($font); + $fontWriter->setColorIndex($this->addColor($font->getColor()->getRGB())); + $this->fontWriters[] = $fontWriter; + + $this->addedFonts[$fontHashCode] = $fontIndex; + } + + return $fontIndex; + } + + /** + * Alter color palette adding a custom color. + * + * @param string $rgb E.g. 'FF00AA' + * + * @return int Color index + */ + private function addColor($rgb) + { + if (!isset($this->colors[$rgb])) { + $color = + [ + hexdec(substr($rgb, 0, 2)), + hexdec(substr($rgb, 2, 2)), + hexdec(substr($rgb, 4)), + 0, + ]; + $colorIndex = array_search($color, $this->palette); + if ($colorIndex) { + $this->colors[$rgb] = $colorIndex; + } else { + if (count($this->colors) === 0) { + $lastColor = 7; + } else { + $lastColor = end($this->colors); + } + if ($lastColor < 57) { + // then we add a custom color altering the palette + $colorIndex = $lastColor + 1; + $this->palette[$colorIndex] = $color; + $this->colors[$rgb] = $colorIndex; + } else { + // no room for more custom colors, just map to black + $colorIndex = 0; + } + } + } else { + // fetch already added custom color + $colorIndex = $this->colors[$rgb]; + } + + return $colorIndex; + } + + /** + * Sets the colour palette to the Excel 97+ default. + */ + private function setPaletteXl97(): void + { + $this->palette = [ + 0x08 => [0x00, 0x00, 0x00, 0x00], + 0x09 => [0xff, 0xff, 0xff, 0x00], + 0x0A => [0xff, 0x00, 0x00, 0x00], + 0x0B => [0x00, 0xff, 0x00, 0x00], + 0x0C => [0x00, 0x00, 0xff, 0x00], + 0x0D => [0xff, 0xff, 0x00, 0x00], + 0x0E => [0xff, 0x00, 0xff, 0x00], + 0x0F => [0x00, 0xff, 0xff, 0x00], + 0x10 => [0x80, 0x00, 0x00, 0x00], + 0x11 => [0x00, 0x80, 0x00, 0x00], + 0x12 => [0x00, 0x00, 0x80, 0x00], + 0x13 => [0x80, 0x80, 0x00, 0x00], + 0x14 => [0x80, 0x00, 0x80, 0x00], + 0x15 => [0x00, 0x80, 0x80, 0x00], + 0x16 => [0xc0, 0xc0, 0xc0, 0x00], + 0x17 => [0x80, 0x80, 0x80, 0x00], + 0x18 => [0x99, 0x99, 0xff, 0x00], + 0x19 => [0x99, 0x33, 0x66, 0x00], + 0x1A => [0xff, 0xff, 0xcc, 0x00], + 0x1B => [0xcc, 0xff, 0xff, 0x00], + 0x1C => [0x66, 0x00, 0x66, 0x00], + 0x1D => [0xff, 0x80, 0x80, 0x00], + 0x1E => [0x00, 0x66, 0xcc, 0x00], + 0x1F => [0xcc, 0xcc, 0xff, 0x00], + 0x20 => [0x00, 0x00, 0x80, 0x00], + 0x21 => [0xff, 0x00, 0xff, 0x00], + 0x22 => [0xff, 0xff, 0x00, 0x00], + 0x23 => [0x00, 0xff, 0xff, 0x00], + 0x24 => [0x80, 0x00, 0x80, 0x00], + 0x25 => [0x80, 0x00, 0x00, 0x00], + 0x26 => [0x00, 0x80, 0x80, 0x00], + 0x27 => [0x00, 0x00, 0xff, 0x00], + 0x28 => [0x00, 0xcc, 0xff, 0x00], + 0x29 => [0xcc, 0xff, 0xff, 0x00], + 0x2A => [0xcc, 0xff, 0xcc, 0x00], + 0x2B => [0xff, 0xff, 0x99, 0x00], + 0x2C => [0x99, 0xcc, 0xff, 0x00], + 0x2D => [0xff, 0x99, 0xcc, 0x00], + 0x2E => [0xcc, 0x99, 0xff, 0x00], + 0x2F => [0xff, 0xcc, 0x99, 0x00], + 0x30 => [0x33, 0x66, 0xff, 0x00], + 0x31 => [0x33, 0xcc, 0xcc, 0x00], + 0x32 => [0x99, 0xcc, 0x00, 0x00], + 0x33 => [0xff, 0xcc, 0x00, 0x00], + 0x34 => [0xff, 0x99, 0x00, 0x00], + 0x35 => [0xff, 0x66, 0x00, 0x00], + 0x36 => [0x66, 0x66, 0x99, 0x00], + 0x37 => [0x96, 0x96, 0x96, 0x00], + 0x38 => [0x00, 0x33, 0x66, 0x00], + 0x39 => [0x33, 0x99, 0x66, 0x00], + 0x3A => [0x00, 0x33, 0x00, 0x00], + 0x3B => [0x33, 0x33, 0x00, 0x00], + 0x3C => [0x99, 0x33, 0x00, 0x00], + 0x3D => [0x99, 0x33, 0x66, 0x00], + 0x3E => [0x33, 0x33, 0x99, 0x00], + 0x3F => [0x33, 0x33, 0x33, 0x00], + ]; + } + + /** + * Assemble worksheets into a workbook and send the BIFF data to an OLE + * storage. + * + * @param array $pWorksheetSizes The sizes in bytes of the binary worksheet streams + * + * @return string Binary data for workbook stream + */ + public function writeWorkbook(array $pWorksheetSizes) + { + $this->worksheetSizes = $pWorksheetSizes; + + // Calculate the number of selected worksheet tabs and call the finalization + // methods for each worksheet + $total_worksheets = $this->spreadsheet->getSheetCount(); + + // Add part 1 of the Workbook globals, what goes before the SHEET records + $this->storeBof(0x0005); + $this->writeCodepage(); + $this->writeWindow1(); + + $this->writeDateMode(); + $this->writeAllFonts(); + $this->writeAllNumberFormats(); + $this->writeAllXfs(); + $this->writeAllStyles(); + $this->writePalette(); + + // Prepare part 3 of the workbook global stream, what goes after the SHEET records + $part3 = ''; + if ($this->countryCode !== -1) { + $part3 .= $this->writeCountry(); + } + $part3 .= $this->writeRecalcId(); + + $part3 .= $this->writeSupbookInternal(); + /* TODO: store external SUPBOOK records and XCT and CRN records + in case of external references for BIFF8 */ + $part3 .= $this->writeExternalsheetBiff8(); + $part3 .= $this->writeAllDefinedNamesBiff8(); + $part3 .= $this->writeMsoDrawingGroup(); + $part3 .= $this->writeSharedStringsTable(); + + $part3 .= $this->writeEof(); + + // Add part 2 of the Workbook globals, the SHEET records + $this->calcSheetOffsets(); + for ($i = 0; $i < $total_worksheets; ++$i) { + $this->writeBoundSheet($this->spreadsheet->getSheet($i), $this->worksheetOffsets[$i]); + } + + // Add part 3 of the Workbook globals + $this->_data .= $part3; + + return $this->_data; + } + + /** + * Calculate offsets for Worksheet BOF records. + */ + private function calcSheetOffsets(): void + { + $boundsheet_length = 10; // fixed length for a BOUNDSHEET record + + // size of Workbook globals part 1 + 3 + $offset = $this->_datasize; + + // add size of Workbook globals part 2, the length of the SHEET records + $total_worksheets = count($this->spreadsheet->getAllSheets()); + foreach ($this->spreadsheet->getWorksheetIterator() as $sheet) { + $offset += $boundsheet_length + strlen(StringHelper::UTF8toBIFF8UnicodeShort($sheet->getTitle())); + } + + // add the sizes of each of the Sheet substreams, respectively + for ($i = 0; $i < $total_worksheets; ++$i) { + $this->worksheetOffsets[$i] = $offset; + $offset += $this->worksheetSizes[$i]; + } + $this->biffSize = $offset; + } + + /** + * Store the Excel FONT records. + */ + private function writeAllFonts(): void + { + foreach ($this->fontWriters as $fontWriter) { + $this->append($fontWriter->writeFont()); + } + } + + /** + * Store user defined numerical formats i.e. FORMAT records. + */ + private function writeAllNumberFormats(): void + { + foreach ($this->numberFormats as $numberFormatIndex => $numberFormat) { + $this->writeNumberFormat($numberFormat->getFormatCode(), $numberFormatIndex); + } + } + + /** + * Write all XF records. + */ + private function writeAllXfs(): void + { + foreach ($this->xfWriters as $xfWriter) { + $this->append($xfWriter->writeXf()); + } + } + + /** + * Write all STYLE records. + */ + private function writeAllStyles(): void + { + $this->writeStyle(); + } + + private function parseDefinedNameValue(DefinedName $pDefinedName): string + { + $definedRange = $pDefinedName->getValue(); + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_CELLREF . '/mui', + $definedRange, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $lengths = array_map('strlen', array_column($splitRanges[0], 0)); + $offsets = array_column($splitRanges[0], 1); + + $worksheets = $splitRanges[2]; + $columns = $splitRanges[6]; + $rows = $splitRanges[7]; + + while ($splitCount > 0) { + --$splitCount; + $length = $lengths[$splitCount]; + $offset = $offsets[$splitCount]; + $worksheet = $worksheets[$splitCount][0]; + $column = $columns[$splitCount][0]; + $row = $rows[$splitCount][0]; + + $newRange = ''; + if (empty($worksheet)) { + if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) { + // We need a worksheet + $worksheet = $pDefinedName->getWorksheet()->getTitle(); + } + } else { + $worksheet = str_replace("''", "'", trim($worksheet, "'")); + } + if (!empty($worksheet)) { + $newRange = "'" . str_replace("'", "''", $worksheet) . "'!"; + } + + if (!empty($column)) { + $newRange .= "\${$column}"; + } + if (!empty($row)) { + $newRange .= "\${$row}"; + } + + $definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length); + } + + return $definedRange; + } + + /** + * Writes all the DEFINEDNAME records (BIFF8). + * So far this is only used for repeating rows/columns (print titles) and print areas. + */ + private function writeAllDefinedNamesBiff8() + { + $chunk = ''; + + // Named ranges + $definedNames = $this->spreadsheet->getDefinedNames(); + if (count($definedNames) > 0) { + // Loop named ranges + foreach ($definedNames as $definedName) { + $range = $this->parseDefinedNameValue($definedName); + + // parse formula + try { + $error = $this->parser->parse($range); + $formulaData = $this->parser->toReversePolish(); + + // make sure tRef3d is of type tRef3dR (0x3A) + if (isset($formulaData[0]) && ($formulaData[0] == "\x7A" || $formulaData[0] == "\x5A")) { + $formulaData = "\x3A" . substr($formulaData, 1); + } + + if ($definedName->getLocalOnly()) { + // local scope + $scope = $this->spreadsheet->getIndex($definedName->getScope()) + 1; + } else { + // global scope + $scope = 0; + } + $chunk .= $this->writeData($this->writeDefinedNameBiff8($definedName->getName(), $formulaData, $scope, false)); + } catch (PhpSpreadsheetException $e) { + // do nothing + } + } + } + + // total number of sheets + $total_worksheets = $this->spreadsheet->getSheetCount(); + + // write the print titles (repeating rows, columns), if any + for ($i = 0; $i < $total_worksheets; ++$i) { + $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup(); + // simultaneous repeatColumns repeatRows + if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) { + $repeat = $sheetSetup->getColumnsToRepeatAtLeft(); + $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1; + $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1; + + $repeat = $sheetSetup->getRowsToRepeatAtTop(); + $rowmin = $repeat[0] - 1; + $rowmax = $repeat[1] - 1; + + // construct formula data manually + $formulaData = pack('Cv', 0x29, 0x17); // tMemFunc + $formulaData .= pack('Cvvvvv', 0x3B, $i, 0, 65535, $colmin, $colmax); // tArea3d + $formulaData .= pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, 0, 255); // tArea3d + $formulaData .= pack('C', 0x10); // tList + + // store the DEFINEDNAME record + $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true)); + + // (exclusive) either repeatColumns or repeatRows + } elseif ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) { + // Columns to repeat + if ($sheetSetup->isColumnsToRepeatAtLeftSet()) { + $repeat = $sheetSetup->getColumnsToRepeatAtLeft(); + $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1; + $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1; + } else { + $colmin = 0; + $colmax = 255; + } + // Rows to repeat + if ($sheetSetup->isRowsToRepeatAtTopSet()) { + $repeat = $sheetSetup->getRowsToRepeatAtTop(); + $rowmin = $repeat[0] - 1; + $rowmax = $repeat[1] - 1; + } else { + $rowmin = 0; + $rowmax = 65535; + } + + // construct formula data manually because parser does not recognize absolute 3d cell references + $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax); + + // store the DEFINEDNAME record + $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true)); + } + } + + // write the print areas, if any + for ($i = 0; $i < $total_worksheets; ++$i) { + $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup(); + if ($sheetSetup->isPrintAreaSet()) { + // Print area, e.g. A3:J6,H1:X20 + $printArea = Coordinate::splitRange($sheetSetup->getPrintArea()); + $countPrintArea = count($printArea); + + $formulaData = ''; + for ($j = 0; $j < $countPrintArea; ++$j) { + $printAreaRect = $printArea[$j]; // e.g. A3:J6 + $printAreaRect[0] = Coordinate::coordinateFromString($printAreaRect[0]); + $printAreaRect[1] = Coordinate::coordinateFromString($printAreaRect[1]); + + $print_rowmin = $printAreaRect[0][1] - 1; + $print_rowmax = $printAreaRect[1][1] - 1; + $print_colmin = Coordinate::columnIndexFromString($printAreaRect[0][0]) - 1; + $print_colmax = Coordinate::columnIndexFromString($printAreaRect[1][0]) - 1; + + // construct formula data manually because parser does not recognize absolute 3d cell references + $formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax); + + if ($j > 0) { + $formulaData .= pack('C', 0x10); // list operator token ',' + } + } + + // store the DEFINEDNAME record + $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true)); + } + } + + // write autofilters, if any + for ($i = 0; $i < $total_worksheets; ++$i) { + $sheetAutoFilter = $this->spreadsheet->getSheet($i)->getAutoFilter(); + $autoFilterRange = $sheetAutoFilter->getRange(); + if (!empty($autoFilterRange)) { + $rangeBounds = Coordinate::rangeBoundaries($autoFilterRange); + + //Autofilter built in name + $name = pack('C', 0x0D); + + $chunk .= $this->writeData($this->writeShortNameBiff8($name, $i + 1, $rangeBounds, true)); + } + } + + return $chunk; + } + + /** + * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data. + * + * @param string $name The name in UTF-8 + * @param string $formulaData The binary formula data + * @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global + * @param bool $isBuiltIn Built-in name? + * + * @return string Complete binary record data + */ + private function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false) + { + $record = 0x0018; + + // option flags + $options = $isBuiltIn ? 0x20 : 0x00; + + // length of the name, character count + $nlen = StringHelper::countCharacters($name); + + // name with stripped length field + $name = substr(StringHelper::UTF8toBIFF8UnicodeLong($name), 2); + + // size of the formula (in bytes) + $sz = strlen($formulaData); + + // combine the parts + $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0) + . $name . $formulaData; + $length = strlen($data); + + $header = pack('vv', $record, $length); + + return $header . $data; + } + + /** + * Write a short NAME record. + * + * @param string $name + * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global + * @param integer[][] $rangeBounds range boundaries + * @param bool $isHidden + * + * @return string Complete binary record data + * */ + private function writeShortNameBiff8($name, $sheetIndex, $rangeBounds, $isHidden = false) + { + $record = 0x0018; + + // option flags + $options = ($isHidden ? 0x21 : 0x00); + + $extra = pack( + 'Cvvvvv', + 0x3B, + $sheetIndex - 1, + $rangeBounds[0][1] - 1, + $rangeBounds[1][1] - 1, + $rangeBounds[0][0] - 1, + $rangeBounds[1][0] - 1 + ); + + // size of the formula (in bytes) + $sz = strlen($extra); + + // combine the parts + $data = pack('vCCvvvCCCCC', $options, 0, 1, $sz, 0, $sheetIndex, 0, 0, 0, 0, 0) + . $name . $extra; + $length = strlen($data); + + $header = pack('vv', $record, $length); + + return $header . $data; + } + + /** + * Stores the CODEPAGE biff record. + */ + private function writeCodepage(): void + { + $record = 0x0042; // Record identifier + $length = 0x0002; // Number of bytes to follow + $cv = $this->codepage; // The code page + + $header = pack('vv', $record, $length); + $data = pack('v', $cv); + + $this->append($header . $data); + } + + /** + * Write Excel BIFF WINDOW1 record. + */ + private function writeWindow1(): void + { + $record = 0x003D; // Record identifier + $length = 0x0012; // Number of bytes to follow + + $xWn = 0x0000; // Horizontal position of window + $yWn = 0x0000; // Vertical position of window + $dxWn = 0x25BC; // Width of window + $dyWn = 0x1572; // Height of window + + $grbit = 0x0038; // Option flags + + // not supported by PhpSpreadsheet, so there is only one selected sheet, the active + $ctabsel = 1; // Number of workbook tabs selected + + $wTabRatio = 0x0258; // Tab to scrollbar ratio + + // not supported by PhpSpreadsheet, set to 0 + $itabFirst = 0; // 1st displayed worksheet + $itabCur = $this->spreadsheet->getActiveSheetIndex(); // Active worksheet + + $header = pack('vv', $record, $length); + $data = pack('vvvvvvvvv', $xWn, $yWn, $dxWn, $dyWn, $grbit, $itabCur, $itabFirst, $ctabsel, $wTabRatio); + $this->append($header . $data); + } + + /** + * Writes Excel BIFF BOUNDSHEET record. + * + * @param Worksheet $sheet Worksheet name + * @param int $offset Location of worksheet BOF + */ + private function writeBoundSheet($sheet, $offset): void + { + $sheetname = $sheet->getTitle(); + $record = 0x0085; // Record identifier + + // sheet state + switch ($sheet->getSheetState()) { + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE: + $ss = 0x00; + + break; + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN: + $ss = 0x01; + + break; + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN: + $ss = 0x02; + + break; + default: + $ss = 0x00; + + break; + } + + // sheet type + $st = 0x00; + + $grbit = 0x0000; // Visibility and sheet type + + $data = pack('VCC', $offset, $ss, $st); + $data .= StringHelper::UTF8toBIFF8UnicodeShort($sheetname); + + $length = strlen($data); + $header = pack('vv', $record, $length); + $this->append($header . $data); + } + + /** + * Write Internal SUPBOOK record. + */ + private function writeSupbookInternal() + { + $record = 0x01AE; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('vv', $this->spreadsheet->getSheetCount(), 0x0401); + + return $this->writeData($header . $data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. + */ + private function writeExternalsheetBiff8() + { + $totalReferences = count($this->parser->references); + $record = 0x0017; // Record identifier + $length = 2 + 6 * $totalReferences; // Number of bytes to follow + + $supbook_index = 0; // FIXME: only using internal SUPBOOK record + $header = pack('vv', $record, $length); + $data = pack('v', $totalReferences); + for ($i = 0; $i < $totalReferences; ++$i) { + $data .= $this->parser->references[$i]; + } + + return $this->writeData($header . $data); + } + + /** + * Write Excel BIFF STYLE records. + */ + private function writeStyle(): void + { + $record = 0x0293; // Record identifier + $length = 0x0004; // Bytes to follow + + $ixfe = 0x8000; // Index to cell style XF + $BuiltIn = 0x00; // Built-in style + $iLevel = 0xff; // Outline style level + + $header = pack('vv', $record, $length); + $data = pack('vCC', $ixfe, $BuiltIn, $iLevel); + $this->append($header . $data); + } + + /** + * Writes Excel FORMAT record for non "built-in" numerical formats. + * + * @param string $format Custom format string + * @param int $ifmt Format index code + */ + private function writeNumberFormat($format, $ifmt): void + { + $record = 0x041E; // Record identifier + + $numberFormatString = StringHelper::UTF8toBIFF8UnicodeLong($format); + $length = 2 + strlen($numberFormatString); // Number of bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', $ifmt) . $numberFormatString; + $this->append($header . $data); + } + + /** + * Write DATEMODE record to indicate the date system in use (1904 or 1900). + */ + private function writeDateMode(): void + { + $record = 0x0022; // Record identifier + $length = 0x0002; // Bytes to follow + + $f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) + ? 1 + : 0; // Flag for 1904 date system + + $header = pack('vv', $record, $length); + $data = pack('v', $f1904); + $this->append($header . $data); + } + + /** + * Stores the COUNTRY record for localization. + * + * @return string + */ + private function writeCountry() + { + $record = 0x008C; // Record identifier + $length = 4; // Number of bytes to follow + + $header = pack('vv', $record, $length); + // using the same country code always for simplicity + $data = pack('vv', $this->countryCode, $this->countryCode); + + return $this->writeData($header . $data); + } + + /** + * Write the RECALCID record. + * + * @return string + */ + private function writeRecalcId() + { + $record = 0x01C1; // Record identifier + $length = 8; // Number of bytes to follow + + $header = pack('vv', $record, $length); + + // by inspection of real Excel files, MS Office Excel 2007 writes this + $data = pack('VV', 0x000001C1, 0x00001E667); + + return $this->writeData($header . $data); + } + + /** + * Stores the PALETTE biff record. + */ + private function writePalette(): void + { + $aref = $this->palette; + + $record = 0x0092; // Record identifier + $length = 2 + 4 * count($aref); // Number of bytes to follow + $ccv = count($aref); // Number of RGB values to follow + $data = ''; // The RGB data + + // Pack the RGB data + foreach ($aref as $color) { + foreach ($color as $byte) { + $data .= pack('C', $byte); + } + } + + $header = pack('vvv', $record, $length, $ccv); + $this->append($header . $data); + } + + /** + * Handling of the SST continue blocks is complicated by the need to include an + * additional continuation byte depending on whether the string is split between + * blocks or whether it starts at the beginning of the block. (There are also + * additional complications that will arise later when/if Rich Strings are + * supported). + * + * The Excel documentation says that the SST record should be followed by an + * EXTSST record. The EXTSST record is a hash table that is used to optimise + * access to SST. However, despite the documentation it doesn't seem to be + * required so we will ignore it. + * + * @return string Binary data + */ + private function writeSharedStringsTable() + { + // maximum size of record data (excluding record header) + $continue_limit = 8224; + + // initialize array of record data blocks + $recordDatas = []; + + // start SST record data block with total number of strings, total number of unique strings + $recordData = pack('VV', $this->stringTotal, $this->stringUnique); + + // loop through all (unique) strings in shared strings table + foreach (array_keys($this->stringTable) as $string) { + // here $string is a BIFF8 encoded string + + // length = character count + $headerinfo = unpack('vlength/Cencoding', $string); + + // currently, this is always 1 = uncompressed + $encoding = $headerinfo['encoding']; + + // initialize finished writing current $string + $finished = false; + + while ($finished === false) { + // normally, there will be only one cycle, but if string cannot immediately be written as is + // there will be need for more than one cylcle, if string longer than one record data block, there + // may be need for even more cycles + + if (strlen($recordData) + strlen($string) <= $continue_limit) { + // then we can write the string (or remainder of string) without any problems + $recordData .= $string; + + if (strlen($recordData) + strlen($string) == $continue_limit) { + // we close the record data block, and initialize a new one + $recordDatas[] = $recordData; + $recordData = ''; + } + + // we are finished writing this string + $finished = true; + } else { + // special treatment writing the string (or remainder of the string) + // If the string is very long it may need to be written in more than one CONTINUE record. + + // check how many bytes more there is room for in the current record + $space_remaining = $continue_limit - strlen($recordData); + + // minimum space needed + // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character + // compressed: 2 byte string length length field + 1 byte option flags + 1 byte character + $min_space_needed = ($encoding == 1) ? 5 : 4; + + // We have two cases + // 1. space remaining is less than minimum space needed + // here we must waste the space remaining and move to next record data block + // 2. space remaining is greater than or equal to minimum space needed + // here we write as much as we can in the current block, then move to next record data block + + // 1. space remaining is less than minimum space needed + if ($space_remaining < $min_space_needed) { + // we close the block, store the block data + $recordDatas[] = $recordData; + + // and start new record data block where we start writing the string + $recordData = ''; + + // 2. space remaining is greater than or equal to minimum space needed + } else { + // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below + $effective_space_remaining = $space_remaining; + + // for uncompressed strings, sometimes effective space remaining is reduced by 1 + if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) { + --$effective_space_remaining; + } + + // one block fininshed, store the block data + $recordData .= substr($string, 0, $effective_space_remaining); + + $string = substr($string, $effective_space_remaining); // for next cycle in while loop + $recordDatas[] = $recordData; + + // start new record data block with the repeated option flags + $recordData = pack('C', $encoding); + } + } + } + } + + // Store the last record data block unless it is empty + // if there was no need for any continue records, this will be the for SST record data block itself + if (strlen($recordData) > 0) { + $recordDatas[] = $recordData; + } + + // combine into one chunk with all the blocks SST, CONTINUE,... + $chunk = ''; + foreach ($recordDatas as $i => $recordData) { + // first block should have the SST record header, remaing should have CONTINUE header + $record = ($i == 0) ? 0x00FC : 0x003C; + + $header = pack('vv', $record, strlen($recordData)); + $data = $header . $recordData; + + $chunk .= $this->writeData($data); + } + + return $chunk; + } + + /** + * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records. + */ + private function writeMsoDrawingGroup() + { + // write the Escher stream if necessary + if (isset($this->escher)) { + $writer = new Escher($this->escher); + $data = $writer->close(); + + $record = 0x00EB; + $length = strlen($data); + $header = pack('vv', $record, $length); + + return $this->writeData($header . $data); + } + + return ''; + } + + /** + * Get Escher object. + * + * @return \PhpOffice\PhpSpreadsheet\Shared\Escher + */ + public function getEscher() + { + return $this->escher; + } + + /** + * Set Escher object. + * + * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue + */ + public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null): void + { + $this->escher = $pValue; + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php new file mode 100644 index 00000000000..a793128ae0b --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -0,0 +1,4491 @@ + +// * +// * The majority of this is _NOT_ my code. I simply ported it from the +// * PERL Spreadsheet::WriteExcel module. +// * +// * The author of the Spreadsheet::WriteExcel module is John McNamara +// * +// * +// * I _DO_ maintain this code, and John McNamara has nothing to do with the +// * porting of this code to PHP. Any questions directly related to this +// * class library should be directed to me. +// * +// * License Information: +// * +// * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +// * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +// * +// * This library is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +// * +// * This library is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public +// * License along with this library; if not, write to the Free Software +// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// */ +class Worksheet extends BIFFwriter +{ + /** + * Formula parser. + * + * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser + */ + private $parser; + + /** + * Maximum number of characters for a string (LABEL record in BIFF5). + * + * @var int + */ + private $xlsStringMaxLength; + + /** + * Array containing format information for columns. + * + * @var array + */ + private $columnInfo; + + /** + * Array containing the selected area for the worksheet. + * + * @var array + */ + private $selection; + + /** + * The active pane for the worksheet. + * + * @var int + */ + private $activePane; + + /** + * Whether to use outline. + * + * @var int + */ + private $outlineOn; + + /** + * Auto outline styles. + * + * @var bool + */ + private $outlineStyle; + + /** + * Whether to have outline summary below. + * + * @var bool + */ + private $outlineBelow; + + /** + * Whether to have outline summary at the right. + * + * @var bool + */ + private $outlineRight; + + /** + * Reference to the total number of strings in the workbook. + * + * @var int + */ + private $stringTotal; + + /** + * Reference to the number of unique strings in the workbook. + * + * @var int + */ + private $stringUnique; + + /** + * Reference to the array containing all the unique strings in the workbook. + * + * @var array + */ + private $stringTable; + + /** + * Color cache. + */ + private $colors; + + /** + * Index of first used row (at least 0). + * + * @var int + */ + private $firstRowIndex; + + /** + * Index of last used row. (no used rows means -1). + * + * @var int + */ + private $lastRowIndex; + + /** + * Index of first used column (at least 0). + * + * @var int + */ + private $firstColumnIndex; + + /** + * Index of last used column (no used columns means -1). + * + * @var int + */ + private $lastColumnIndex; + + /** + * Sheet object. + * + * @var \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet + */ + public $phpSheet; + + /** + * Count cell style Xfs. + * + * @var int + */ + private $countCellStyleXfs; + + /** + * Escher object corresponding to MSODRAWING. + * + * @var \PhpOffice\PhpSpreadsheet\Shared\Escher + */ + private $escher; + + /** + * Array of font hashes associated to FONT records index. + * + * @var array + */ + public $fontHashIndex; + + /** + * @var bool + */ + private $preCalculateFormulas; + + /** + * @var int + */ + private $printHeaders; + + /** + * Constructor. + * + * @param int $str_total Total number of strings + * @param int $str_unique Total number of unique strings + * @param array &$str_table String Table + * @param array &$colors Colour Table + * @param Parser $parser The formula parser created for the Workbook + * @param bool $preCalculateFormulas Flag indicating whether formulas should be calculated or just written + * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet The worksheet to write + */ + public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, Parser $parser, $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet) + { + // It needs to call its parent's constructor explicitly + parent::__construct(); + + $this->preCalculateFormulas = $preCalculateFormulas; + $this->stringTotal = &$str_total; + $this->stringUnique = &$str_unique; + $this->stringTable = &$str_table; + $this->colors = &$colors; + $this->parser = $parser; + + $this->phpSheet = $phpSheet; + + $this->xlsStringMaxLength = 255; + $this->columnInfo = []; + $this->selection = [0, 0, 0, 0]; + $this->activePane = 3; + + $this->printHeaders = 0; + + $this->outlineStyle = 0; + $this->outlineBelow = 1; + $this->outlineRight = 1; + $this->outlineOn = 1; + + $this->fontHashIndex = []; + + // calculate values for DIMENSIONS record + $minR = 1; + $minC = 'A'; + + $maxR = $this->phpSheet->getHighestRow(); + $maxC = $this->phpSheet->getHighestColumn(); + + // Determine lowest and highest column and row + $this->lastRowIndex = ($maxR > 65535) ? 65535 : $maxR; + + $this->firstColumnIndex = Coordinate::columnIndexFromString($minC); + $this->lastColumnIndex = Coordinate::columnIndexFromString($maxC); + +// if ($this->firstColumnIndex > 255) $this->firstColumnIndex = 255; + if ($this->lastColumnIndex > 255) { + $this->lastColumnIndex = 255; + } + + $this->countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection()); + } + + /** + * Add data to the beginning of the workbook (note the reverse order) + * and to the end of the workbook. + * + * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::storeWorkbook() + */ + public function close(): void + { + $phpSheet = $this->phpSheet; + + // Storing selected cells and active sheet because it changes while parsing cells with formulas. + $selectedCells = $this->phpSheet->getSelectedCells(); + $activeSheetIndex = $this->phpSheet->getParent()->getActiveSheetIndex(); + + // Write BOF record + $this->storeBof(0x0010); + + // Write PRINTHEADERS + $this->writePrintHeaders(); + + // Write PRINTGRIDLINES + $this->writePrintGridlines(); + + // Write GRIDSET + $this->writeGridset(); + + // Calculate column widths + $phpSheet->calculateColumnWidths(); + + // Column dimensions + if (($defaultWidth = $phpSheet->getDefaultColumnDimension()->getWidth()) < 0) { + $defaultWidth = \PhpOffice\PhpSpreadsheet\Shared\Font::getDefaultColumnWidthByFont($phpSheet->getParent()->getDefaultStyle()->getFont()); + } + + $columnDimensions = $phpSheet->getColumnDimensions(); + $maxCol = $this->lastColumnIndex - 1; + for ($i = 0; $i <= $maxCol; ++$i) { + $hidden = 0; + $level = 0; + $xfIndex = 15; // there are 15 cell style Xfs + + $width = $defaultWidth; + + $columnLetter = Coordinate::stringFromColumnIndex($i + 1); + if (isset($columnDimensions[$columnLetter])) { + $columnDimension = $columnDimensions[$columnLetter]; + if ($columnDimension->getWidth() >= 0) { + $width = $columnDimension->getWidth(); + } + $hidden = $columnDimension->getVisible() ? 0 : 1; + $level = $columnDimension->getOutlineLevel(); + $xfIndex = $columnDimension->getXfIndex() + 15; // there are 15 cell style Xfs + } + + // Components of columnInfo: + // $firstcol first column on the range + // $lastcol last column on the range + // $width width to set + // $xfIndex The optional cell style Xf index to apply to the columns + // $hidden The optional hidden atribute + // $level The optional outline level + $this->columnInfo[] = [$i, $i, $width, $xfIndex, $hidden, $level]; + } + + // Write GUTS + $this->writeGuts(); + + // Write DEFAULTROWHEIGHT + $this->writeDefaultRowHeight(); + // Write WSBOOL + $this->writeWsbool(); + // Write horizontal and vertical page breaks + $this->writeBreaks(); + // Write page header + $this->writeHeader(); + // Write page footer + $this->writeFooter(); + // Write page horizontal centering + $this->writeHcenter(); + // Write page vertical centering + $this->writeVcenter(); + // Write left margin + $this->writeMarginLeft(); + // Write right margin + $this->writeMarginRight(); + // Write top margin + $this->writeMarginTop(); + // Write bottom margin + $this->writeMarginBottom(); + // Write page setup + $this->writeSetup(); + // Write sheet protection + $this->writeProtect(); + // Write SCENPROTECT + $this->writeScenProtect(); + // Write OBJECTPROTECT + $this->writeObjectProtect(); + // Write sheet password + $this->writePassword(); + // Write DEFCOLWIDTH record + $this->writeDefcol(); + + // Write the COLINFO records if they exist + if (!empty($this->columnInfo)) { + $colcount = count($this->columnInfo); + for ($i = 0; $i < $colcount; ++$i) { + $this->writeColinfo($this->columnInfo[$i]); + } + } + $autoFilterRange = $phpSheet->getAutoFilter()->getRange(); + if (!empty($autoFilterRange)) { + // Write AUTOFILTERINFO + $this->writeAutoFilterInfo(); + } + + // Write sheet dimensions + $this->writeDimensions(); + + // Row dimensions + foreach ($phpSheet->getRowDimensions() as $rowDimension) { + $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs + $this->writeRow($rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel()); + } + + // Write Cells + foreach ($phpSheet->getCoordinates() as $coordinate) { + $cell = $phpSheet->getCell($coordinate); + $row = $cell->getRow() - 1; + $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1; + + // Don't break Excel break the code! + if ($row > 65535 || $column > 255) { + throw new WriterException('Rows or columns overflow! Excel5 has limit to 65535 rows and 255 columns. Use XLSX instead.'); + } + + // Write cell value + $xfIndex = $cell->getXfIndex() + 15; // there are 15 cell style Xfs + + $cVal = $cell->getValue(); + if ($cVal instanceof RichText) { + $arrcRun = []; + $str_len = StringHelper::countCharacters($cVal->getPlainText(), 'UTF-8'); + $str_pos = 0; + $elements = $cVal->getRichTextElements(); + foreach ($elements as $element) { + // FONT Index + if ($element instanceof Run) { + $str_fontidx = $this->fontHashIndex[$element->getFont()->getHashCode()]; + } else { + $str_fontidx = 0; + } + $arrcRun[] = ['strlen' => $str_pos, 'fontidx' => $str_fontidx]; + // Position FROM + $str_pos += StringHelper::countCharacters($element->getText(), 'UTF-8'); + } + $this->writeRichTextString($row, $column, $cVal->getPlainText(), $xfIndex, $arrcRun); + } else { + switch ($cell->getDatatype()) { + case DataType::TYPE_STRING: + case DataType::TYPE_NULL: + if ($cVal === '' || $cVal === null) { + $this->writeBlank($row, $column, $xfIndex); + } else { + $this->writeString($row, $column, $cVal, $xfIndex); + } + + break; + case DataType::TYPE_NUMERIC: + $this->writeNumber($row, $column, $cVal, $xfIndex); + + break; + case DataType::TYPE_FORMULA: + $calculatedValue = $this->preCalculateFormulas ? + $cell->getCalculatedValue() : null; + if (self::WRITE_FORMULA_EXCEPTION == $this->writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue)) { + if ($calculatedValue === null) { + $calculatedValue = $cell->getCalculatedValue(); + } + $calctype = gettype($calculatedValue); + switch ($calctype) { + case 'integer': + case 'double': + $this->writeNumber($row, $column, $calculatedValue, $xfIndex); + + break; + case 'string': + $this->writeString($row, $column, $calculatedValue, $xfIndex); + + break; + case 'boolean': + $this->writeBoolErr($row, $column, $calculatedValue, 0, $xfIndex); + + break; + default: + $this->writeString($row, $column, $cVal, $xfIndex); + } + } + + break; + case DataType::TYPE_BOOL: + $this->writeBoolErr($row, $column, $cVal, 0, $xfIndex); + + break; + case DataType::TYPE_ERROR: + $this->writeBoolErr($row, $column, self::mapErrorCode($cVal), 1, $xfIndex); + + break; + } + } + } + + // Append + $this->writeMsoDrawing(); + + // Restoring active sheet. + $this->phpSheet->getParent()->setActiveSheetIndex($activeSheetIndex); + + // Write WINDOW2 record + $this->writeWindow2(); + + // Write PLV record + $this->writePageLayoutView(); + + // Write ZOOM record + $this->writeZoom(); + if ($phpSheet->getFreezePane()) { + $this->writePanes(); + } + + // Restoring selected cells. + $this->phpSheet->setSelectedCells($selectedCells); + + // Write SELECTION record + $this->writeSelection(); + + // Write MergedCellsTable Record + $this->writeMergedCells(); + + // Hyperlinks + foreach ($phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) { + [$column, $row] = Coordinate::coordinateFromString($coordinate); + + $url = $hyperlink->getUrl(); + + if (strpos($url, 'sheet://') !== false) { + // internal to current workbook + $url = str_replace('sheet://', 'internal:', $url); + } elseif (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) { + // URL + } else { + // external (local file) + $url = 'external:' . $url; + } + + $this->writeUrl($row - 1, Coordinate::columnIndexFromString($column) - 1, $url); + } + + $this->writeDataValidity(); + $this->writeSheetLayout(); + + // Write SHEETPROTECTION record + $this->writeSheetProtection(); + $this->writeRangeProtection(); + + $arrConditionalStyles = $phpSheet->getConditionalStylesCollection(); + if (!empty($arrConditionalStyles)) { + $arrConditional = []; + // @TODO CFRule & CFHeader + // Write CFHEADER record + $this->writeCFHeader(); + // Write ConditionalFormattingTable records + foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) { + foreach ($conditionalStyles as $conditional) { + if ( + $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION + || $conditional->getConditionType() == Conditional::CONDITION_CELLIS + ) { + if (!isset($arrConditional[$conditional->getHashCode()])) { + // This hash code has been handled + $arrConditional[$conditional->getHashCode()] = true; + + // Write CFRULE record + $this->writeCFRule($conditional); + } + } + } + } + } + + $this->storeEof(); + } + + /** + * Write a cell range address in BIFF8 + * always fixed range + * See section 2.5.14 in OpenOffice.org's Documentation of the Microsoft Excel File Format. + * + * @param string $range E.g. 'A1' or 'A1:B6' + * + * @return string Binary data + */ + private function writeBIFF8CellRangeAddressFixed($range) + { + $explodes = explode(':', $range); + + // extract first cell, e.g. 'A1' + $firstCell = $explodes[0]; + + // extract last cell, e.g. 'B6' + if (count($explodes) == 1) { + $lastCell = $firstCell; + } else { + $lastCell = $explodes[1]; + } + + $firstCellCoordinates = Coordinate::coordinateFromString($firstCell); // e.g. [0, 1] + $lastCellCoordinates = Coordinate::coordinateFromString($lastCell); // e.g. [1, 6] + + return pack('vvvv', $firstCellCoordinates[1] - 1, $lastCellCoordinates[1] - 1, Coordinate::columnIndexFromString($firstCellCoordinates[0]) - 1, Coordinate::columnIndexFromString($lastCellCoordinates[0]) - 1); + } + + /** + * Retrieves data from memory in one chunk, or from disk in $buffer + * sized chunks. + * + * @return string The data + */ + public function getData() + { + $buffer = 4096; + + // Return data stored in memory + if (isset($this->_data)) { + $tmp = $this->_data; + $this->_data = null; + + return $tmp; + } + + // No data to return + return false; + } + + /** + * Set the option to print the row and column headers on the printed page. + * + * @param int $print Whether to print the headers or not. Defaults to 1 (print). + */ + public function printRowColHeaders($print = 1): void + { + $this->printHeaders = $print; + } + + /** + * This method sets the properties for outlining and grouping. The defaults + * correspond to Excel's defaults. + * + * @param bool $visible + * @param bool $symbols_below + * @param bool $symbols_right + * @param bool $auto_style + */ + public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false): void + { + $this->outlineOn = $visible; + $this->outlineBelow = $symbols_below; + $this->outlineRight = $symbols_right; + $this->outlineStyle = $auto_style; + + // Ensure this is a boolean vale for Window2 + if ($this->outlineOn) { + $this->outlineOn = 1; + } + } + + /** + * Write a double to the specified row and column (zero indexed). + * An integer can be written as a double. Excel will display an + * integer. $format is optional. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * + * @param int $row Zero indexed row + * @param int $col Zero indexed column + * @param float $num The number to write + * @param mixed $xfIndex The optional XF format + * + * @return int + */ + private function writeNumber($row, $col, $num, $xfIndex) + { + $record = 0x0203; // Record identifier + $length = 0x000E; // Number of bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('vvv', $row, $col, $xfIndex); + $xl_double = pack('d', $num); + if (self::getByteOrder()) { // if it's Big Endian + $xl_double = strrev($xl_double); + } + + $this->append($header . $data . $xl_double); + + return 0; + } + + /** + * Write a LABELSST record or a LABEL record. Which one depends on BIFF version. + * + * @param int $row Row index (0-based) + * @param int $col Column index (0-based) + * @param string $str The string + * @param int $xfIndex Index to XF record + */ + private function writeString($row, $col, $str, $xfIndex): void + { + $this->writeLabelSst($row, $col, $str, $xfIndex); + } + + /** + * Write a LABELSST record or a LABEL record. Which one depends on BIFF version + * It differs from writeString by the writing of rich text strings. + * + * @param int $row Row index (0-based) + * @param int $col Column index (0-based) + * @param string $str The string + * @param int $xfIndex The XF format index for the cell + * @param array $arrcRun Index to Font record and characters beginning + */ + private function writeRichTextString($row, $col, $str, $xfIndex, $arrcRun): void + { + $record = 0x00FD; // Record identifier + $length = 0x000A; // Bytes to follow + $str = StringHelper::UTF8toBIFF8UnicodeShort($str, $arrcRun); + + // check if string is already present + if (!isset($this->stringTable[$str])) { + $this->stringTable[$str] = $this->stringUnique++; + } + ++$this->stringTotal; + + $header = pack('vv', $record, $length); + $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]); + $this->append($header . $data); + } + + /** + * Write a string to the specified row and column (zero indexed). + * This is the BIFF8 version (no 255 chars limit). + * $format is optional. + * + * @param int $row Zero indexed row + * @param int $col Zero indexed column + * @param string $str The string to write + * @param mixed $xfIndex The XF format index for the cell + */ + private function writeLabelSst($row, $col, $str, $xfIndex): void + { + $record = 0x00FD; // Record identifier + $length = 0x000A; // Bytes to follow + + $str = StringHelper::UTF8toBIFF8UnicodeLong($str); + + // check if string is already present + if (!isset($this->stringTable[$str])) { + $this->stringTable[$str] = $this->stringUnique++; + } + ++$this->stringTotal; + + $header = pack('vv', $record, $length); + $data = pack('vvvV', $row, $col, $xfIndex, $this->stringTable[$str]); + $this->append($header . $data); + } + + /** + * Write a blank cell to the specified row and column (zero indexed). + * A blank cell is used to specify formatting without adding a string + * or a number. + * + * A blank cell without a format serves no purpose. Therefore, we don't write + * a BLANK record unless a format is specified. + * + * Returns 0 : normal termination (including no format) + * -1 : insufficient number of arguments + * -2 : row or column out of range + * + * @param int $row Zero indexed row + * @param int $col Zero indexed column + * @param mixed $xfIndex The XF format index + * + * @return int + */ + public function writeBlank($row, $col, $xfIndex) + { + $record = 0x0201; // Record identifier + $length = 0x0006; // Number of bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('vvv', $row, $col, $xfIndex); + $this->append($header . $data); + + return 0; + } + + /** + * Write a boolean or an error type to the specified row and column (zero indexed). + * + * @param int $row Row index (0-based) + * @param int $col Column index (0-based) + * @param int $value + * @param bool $isError Error or Boolean? + * @param int $xfIndex + * + * @return int + */ + private function writeBoolErr($row, $col, $value, $isError, $xfIndex) + { + $record = 0x0205; + $length = 8; + + $header = pack('vv', $record, $length); + $data = pack('vvvCC', $row, $col, $xfIndex, $value, $isError); + $this->append($header . $data); + + return 0; + } + + const WRITE_FORMULA_NORMAL = 0; + const WRITE_FORMULA_ERRORS = -1; + const WRITE_FORMULA_RANGE = -2; + const WRITE_FORMULA_EXCEPTION = -3; + + /** + * Write a formula to the specified row and column (zero indexed). + * The textual representation of the formula is passed to the parser in + * Parser.php which returns a packed binary string. + * + * Returns 0 : WRITE_FORMULA_NORMAL normal termination + * -1 : WRITE_FORMULA_ERRORS formula errors (bad formula) + * -2 : WRITE_FORMULA_RANGE row or column out of range + * -3 : WRITE_FORMULA_EXCEPTION parse raised exception, probably due to definedname + * + * @param int $row Zero indexed row + * @param int $col Zero indexed column + * @param string $formula The formula text string + * @param mixed $xfIndex The XF format index + * @param mixed $calculatedValue Calculated value + * + * @return int + */ + private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue) + { + $record = 0x0006; // Record identifier + // Initialize possible additional value for STRING record that should be written after the FORMULA record? + $stringValue = null; + + // calculated value + if (isset($calculatedValue)) { + // Since we can't yet get the data type of the calculated value, + // we use best effort to determine data type + if (is_bool($calculatedValue)) { + // Boolean value + $num = pack('CCCvCv', 0x01, 0x00, (int) $calculatedValue, 0x00, 0x00, 0xFFFF); + } elseif (is_int($calculatedValue) || is_float($calculatedValue)) { + // Numeric value + $num = pack('d', $calculatedValue); + } elseif (is_string($calculatedValue)) { + $errorCodes = DataType::getErrorCodes(); + if (isset($errorCodes[$calculatedValue])) { + // Error value + $num = pack('CCCvCv', 0x02, 0x00, self::mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF); + } elseif ($calculatedValue === '') { + // Empty string (and BIFF8) + $num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF); + } else { + // Non-empty string value (or empty string BIFF5) + $stringValue = $calculatedValue; + $num = pack('CCCvCv', 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF); + } + } else { + // We are really not supposed to reach here + $num = pack('d', 0x00); + } + } else { + $num = pack('d', 0x00); + } + + $grbit = 0x03; // Option flags + $unknown = 0x0000; // Must be zero + + // Strip the '=' or '@' sign at the beginning of the formula string + if ($formula[0] == '=') { + $formula = substr($formula, 1); + } else { + // Error handling + $this->writeString($row, $col, 'Unrecognised character for formula', 0); + + return self::WRITE_FORMULA_ERRORS; + } + + // Parse the formula using the parser in Parser.php + try { + $error = $this->parser->parse($formula); + $formula = $this->parser->toReversePolish(); + + $formlen = strlen($formula); // Length of the binary string + $length = 0x16 + $formlen; // Length of the record data + + $header = pack('vv', $record, $length); + + $data = pack('vvv', $row, $col, $xfIndex) + . $num + . pack('vVv', $grbit, $unknown, $formlen); + $this->append($header . $data . $formula); + + // Append also a STRING record if necessary + if ($stringValue !== null) { + $this->writeStringRecord($stringValue); + } + + return self::WRITE_FORMULA_NORMAL; + } catch (PhpSpreadsheetException $e) { + return self::WRITE_FORMULA_EXCEPTION; + } + } + + /** + * Write a STRING record. This. + * + * @param string $stringValue + */ + private function writeStringRecord($stringValue): void + { + $record = 0x0207; // Record identifier + $data = StringHelper::UTF8toBIFF8UnicodeLong($stringValue); + + $length = strlen($data); + $header = pack('vv', $record, $length); + + $this->append($header . $data); + } + + /** + * Write a hyperlink. + * This is comprised of two elements: the visible label and + * the invisible link. The visible label is the same as the link unless an + * alternative string is specified. The label is written using the + * writeString() method. Therefore the 255 characters string limit applies. + * $string and $format are optional. + * + * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external + * directory url. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @param int $row Row + * @param int $col Column + * @param string $url URL string + * + * @return int + */ + private function writeUrl($row, $col, $url) + { + // Add start row and col to arg list + return $this->writeUrlRange($row, $col, $row, $col, $url); + } + + /** + * This is the more general form of writeUrl(). It allows a hyperlink to be + * written to a range of cells. This function also decides the type of hyperlink + * to be written. These are either, Web (http, ftp, mailto), Internal + * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). + * + * @param int $row1 Start row + * @param int $col1 Start column + * @param int $row2 End row + * @param int $col2 End column + * @param string $url URL string + * + * @return int + * + * @see writeUrl() + */ + public function writeUrlRange($row1, $col1, $row2, $col2, $url) + { + // Check for internal/external sheet links or default to web link + if (preg_match('[^internal:]', $url)) { + return $this->writeUrlInternal($row1, $col1, $row2, $col2, $url); + } + if (preg_match('[^external:]', $url)) { + return $this->writeUrlExternal($row1, $col1, $row2, $col2, $url); + } + + return $this->writeUrlWeb($row1, $col1, $row2, $col2, $url); + } + + /** + * Used to write http, ftp and mailto hyperlinks. + * The link type ($options) is 0x03 is the same as absolute dir ref without + * sheet. However it is differentiated by the $unknown2 data stream. + * + * @param int $row1 Start row + * @param int $col1 Start column + * @param int $row2 End row + * @param int $col2 End column + * @param string $url URL string + * + * @return int + * + * @see writeUrl() + */ + public function writeUrlWeb($row1, $col1, $row2, $col2, $url) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); + $unknown2 = pack('H*', 'E0C9EA79F9BACE118C8200AA004BA90B'); + + // Pack the option flags + $options = pack('V', 0x03); + + // Convert URL to a null terminated wchar string + $url = implode("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL + $url_len = pack('V', strlen($url)); + + // Calculate the data length + $length = 0x34 + strlen($url); + + // Pack the header data + $header = pack('vv', $record, $length); + $data = pack('vvvv', $row1, $row2, $col1, $col2); + + // Write the packed data + $this->append($header . $data . $unknown1 . $options . $unknown2 . $url_len . $url); + + return 0; + } + + /** + * Used to write internal reference hyperlinks such as "Sheet1!A1". + * + * @param int $row1 Start row + * @param int $col1 Start column + * @param int $row2 End row + * @param int $col2 End column + * @param string $url URL string + * + * @return int + * + * @see writeUrl() + */ + public function writeUrlInternal($row1, $col1, $row2, $col2, $url) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + // Strip URL type + $url = preg_replace('/^internal:/', '', $url); + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); + + // Pack the option flags + $options = pack('V', 0x08); + + // Convert the URL type and to a null terminated wchar string + $url .= "\0"; + + // character count + $url_len = StringHelper::countCharacters($url); + $url_len = pack('V', $url_len); + + $url = StringHelper::convertEncoding($url, 'UTF-16LE', 'UTF-8'); + + // Calculate the data length + $length = 0x24 + strlen($url); + + // Pack the header data + $header = pack('vv', $record, $length); + $data = pack('vvvv', $row1, $row2, $col1, $col2); + + // Write the packed data + $this->append($header . $data . $unknown1 . $options . $url_len . $url); + + return 0; + } + + /** + * Write links to external directory names such as 'c:\foo.xls', + * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. + * + * Note: Excel writes some relative links with the $dir_long string. We ignore + * these cases for the sake of simpler code. + * + * @param int $row1 Start row + * @param int $col1 Start column + * @param int $row2 End row + * @param int $col2 End column + * @param string $url URL string + * + * @return int + * + * @see writeUrl() + */ + public function writeUrlExternal($row1, $col1, $row2, $col2, $url) + { + // Network drives are different. We will handle them separately + // MS/Novell network drives and shares start with \\ + if (preg_match('[^external:\\\\]', $url)) { + return; //($this->writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format)); + } + + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + // Strip URL type and change Unix dir separator to Dos style (if needed) + // + $url = preg_replace('/^external:/', '', $url); + $url = preg_replace('/\//', '\\', $url); + + // Determine if the link is relative or absolute: + // relative if link contains no dir separator, "somefile.xls" + // relative if link starts with up-dir, "..\..\somefile.xls" + // otherwise, absolute + + $absolute = 0x00; // relative path + if (preg_match('/^[A-Z]:/', $url)) { + $absolute = 0x02; // absolute path on Windows, e.g. C:\... + } + $link_type = 0x01 | $absolute; + + // Determine if the link contains a sheet reference and change some of the + // parameters accordingly. + // Split the dir name and sheet name (if it exists) + $dir_long = $url; + if (preg_match('/\\#/', $url)) { + $link_type |= 0x08; + } + + // Pack the link type + $link_type = pack('V', $link_type); + + // Calculate the up-level dir count e.g.. (..\..\..\ == 3) + $up_count = preg_match_all('/\\.\\.\\\\/', $dir_long, $useless); + $up_count = pack('v', $up_count); + + // Store the short dos dir name (null terminated) + $dir_short = preg_replace('/\\.\\.\\\\/', '', $dir_long) . "\0"; + + // Store the long dir name as a wchar string (non-null terminated) + $dir_long = $dir_long . "\0"; + + // Pack the lengths of the dir strings + $dir_short_len = pack('V', strlen($dir_short)); + $dir_long_len = pack('V', strlen($dir_long)); + $stream_len = pack('V', 0); //strlen($dir_long) + 0x06); + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); + $unknown2 = pack('H*', '0303000000000000C000000000000046'); + $unknown3 = pack('H*', 'FFFFADDE000000000000000000000000000000000000000'); + $unknown4 = pack('v', 0x03); + + // Pack the main data stream + $data = pack('vvvv', $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $unknown2 . + $up_count . + $dir_short_len . + $dir_short . + $unknown3 . + $stream_len; /*. + $dir_long_len . + $unknown4 . + $dir_long . + $sheet_len . + $sheet ;*/ + + // Pack the header data + $length = strlen($data); + $header = pack('vv', $record, $length); + + // Write the packed data + $this->append($header . $data); + + return 0; + } + + /** + * This method is used to set the height and format for a row. + * + * @param int $row The row to set + * @param int $height Height we are giving to the row. + * Use null to set XF without setting height + * @param int $xfIndex The optional cell style Xf index to apply to the columns + * @param bool $hidden The optional hidden attribute + * @param int $level The optional outline level for row, in range [0,7] + */ + private function writeRow($row, $height, $xfIndex, $hidden = false, $level = 0): void + { + $record = 0x0208; // Record identifier + $length = 0x0010; // Number of bytes to follow + + $colMic = 0x0000; // First defined column + $colMac = 0x0000; // Last defined column + $irwMac = 0x0000; // Used by Excel to optimise loading + $reserved = 0x0000; // Reserved + $grbit = 0x0000; // Option flags + $ixfe = $xfIndex; + + if ($height < 0) { + $height = null; + } + + // Use writeRow($row, null, $XF) to set XF format without setting height + if ($height != null) { + $miyRw = $height * 20; // row height + } else { + $miyRw = 0xff; // default row height is 256 + } + + // Set the options flags. fUnsynced is used to show that the font and row + // heights are not compatible. This is usually the case for WriteExcel. + // The collapsed flag 0x10 doesn't seem to be used to indicate that a row + // is collapsed. Instead it is used to indicate that the previous row is + // collapsed. The zero height flag, 0x20, is used to collapse a row. + + $grbit |= $level; + if ($hidden) { + $grbit |= 0x0030; + } + if ($height !== null) { + $grbit |= 0x0040; // fUnsynced + } + if ($xfIndex !== 0xF) { + $grbit |= 0x0080; + } + $grbit |= 0x0100; + + $header = pack('vv', $record, $length); + $data = pack('vvvvvvvv', $row, $colMic, $colMac, $miyRw, $irwMac, $reserved, $grbit, $ixfe); + $this->append($header . $data); + } + + /** + * Writes Excel DIMENSIONS to define the area in which there is data. + */ + private function writeDimensions(): void + { + $record = 0x0200; // Record identifier + + $length = 0x000E; + $data = pack('VVvvv', $this->firstRowIndex, $this->lastRowIndex + 1, $this->firstColumnIndex, $this->lastColumnIndex + 1, 0x0000); // reserved + + $header = pack('vv', $record, $length); + $this->append($header . $data); + } + + /** + * Write BIFF record Window2. + */ + private function writeWindow2(): void + { + $record = 0x023E; // Record identifier + $length = 0x0012; + + $grbit = 0x00B6; // Option flags + $rwTop = 0x0000; // Top row visible in window + $colLeft = 0x0000; // Leftmost column visible in window + + // The options flags that comprise $grbit + $fDspFmla = 0; // 0 - bit + $fDspGrid = $this->phpSheet->getShowGridlines() ? 1 : 0; // 1 + $fDspRwCol = $this->phpSheet->getShowRowColHeaders() ? 1 : 0; // 2 + $fFrozen = $this->phpSheet->getFreezePane() ? 1 : 0; // 3 + $fDspZeros = 1; // 4 + $fDefaultHdr = 1; // 5 + $fArabic = $this->phpSheet->getRightToLeft() ? 1 : 0; // 6 + $fDspGuts = $this->outlineOn; // 7 + $fFrozenNoSplit = 0; // 0 - bit + // no support in PhpSpreadsheet for selected sheet, therefore sheet is only selected if it is the active sheet + $fSelected = ($this->phpSheet === $this->phpSheet->getParent()->getActiveSheet()) ? 1 : 0; + $fPageBreakPreview = $this->phpSheet->getSheetView()->getView() === SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW; + + $grbit = $fDspFmla; + $grbit |= $fDspGrid << 1; + $grbit |= $fDspRwCol << 2; + $grbit |= $fFrozen << 3; + $grbit |= $fDspZeros << 4; + $grbit |= $fDefaultHdr << 5; + $grbit |= $fArabic << 6; + $grbit |= $fDspGuts << 7; + $grbit |= $fFrozenNoSplit << 8; + $grbit |= $fSelected << 9; // Selected sheets. + $grbit |= $fSelected << 10; // Active sheet. + $grbit |= $fPageBreakPreview << 11; + + $header = pack('vv', $record, $length); + $data = pack('vvv', $grbit, $rwTop, $colLeft); + + // FIXME !!! + $rgbHdr = 0x0040; // Row/column heading and gridline color index + $zoom_factor_page_break = ($fPageBreakPreview ? $this->phpSheet->getSheetView()->getZoomScale() : 0x0000); + $zoom_factor_normal = $this->phpSheet->getSheetView()->getZoomScaleNormal(); + + $data .= pack('vvvvV', $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000); + + $this->append($header . $data); + } + + /** + * Write BIFF record DEFAULTROWHEIGHT. + */ + private function writeDefaultRowHeight(): void + { + $defaultRowHeight = $this->phpSheet->getDefaultRowDimension()->getRowHeight(); + + if ($defaultRowHeight < 0) { + return; + } + + // convert to twips + $defaultRowHeight = (int) 20 * $defaultRowHeight; + + $record = 0x0225; // Record identifier + $length = 0x0004; // Number of bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('vv', 1, $defaultRowHeight); + $this->append($header . $data); + } + + /** + * Write BIFF record DEFCOLWIDTH if COLINFO records are in use. + */ + private function writeDefcol(): void + { + $defaultColWidth = 8; + + $record = 0x0055; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', $defaultColWidth); + $this->append($header . $data); + } + + /** + * Write BIFF record COLINFO to define column widths. + * + * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C + * length record. + * + * @param array $col_array This is the only parameter received and is composed of the following: + * 0 => First formatted column, + * 1 => Last formatted column, + * 2 => Col width (8.43 is Excel default), + * 3 => The optional XF format of the column, + * 4 => Option flags. + * 5 => Optional outline level + */ + private function writeColinfo($col_array): void + { + if (isset($col_array[0])) { + $colFirst = $col_array[0]; + } + if (isset($col_array[1])) { + $colLast = $col_array[1]; + } + if (isset($col_array[2])) { + $coldx = $col_array[2]; + } else { + $coldx = 8.43; + } + if (isset($col_array[3])) { + $xfIndex = $col_array[3]; + } else { + $xfIndex = 15; + } + if (isset($col_array[4])) { + $grbit = $col_array[4]; + } else { + $grbit = 0; + } + if (isset($col_array[5])) { + $level = $col_array[5]; + } else { + $level = 0; + } + $record = 0x007D; // Record identifier + $length = 0x000C; // Number of bytes to follow + + $coldx *= 256; // Convert to units of 1/256 of a char + + $ixfe = $xfIndex; + $reserved = 0x0000; // Reserved + + $level = max(0, min($level, 7)); + $grbit |= $level << 8; + + $header = pack('vv', $record, $length); + $data = pack('vvvvvv', $colFirst, $colLast, $coldx, $ixfe, $grbit, $reserved); + $this->append($header . $data); + } + + /** + * Write BIFF record SELECTION. + */ + private function writeSelection(): void + { + // look up the selected cell range + $selectedCells = Coordinate::splitRange($this->phpSheet->getSelectedCells()); + $selectedCells = $selectedCells[0]; + if (count($selectedCells) == 2) { + [$first, $last] = $selectedCells; + } else { + $first = $selectedCells[0]; + $last = $selectedCells[0]; + } + + [$colFirst, $rwFirst] = Coordinate::coordinateFromString($first); + $colFirst = Coordinate::columnIndexFromString($colFirst) - 1; // base 0 column index + --$rwFirst; // base 0 row index + + [$colLast, $rwLast] = Coordinate::coordinateFromString($last); + $colLast = Coordinate::columnIndexFromString($colLast) - 1; // base 0 column index + --$rwLast; // base 0 row index + + // make sure we are not out of bounds + $colFirst = min($colFirst, 255); + $colLast = min($colLast, 255); + + $rwFirst = min($rwFirst, 65535); + $rwLast = min($rwLast, 65535); + + $record = 0x001D; // Record identifier + $length = 0x000F; // Number of bytes to follow + + $pnn = $this->activePane; // Pane position + $rwAct = $rwFirst; // Active row + $colAct = $colFirst; // Active column + $irefAct = 0; // Active cell ref + $cref = 1; // Number of refs + + if (!isset($rwLast)) { + $rwLast = $rwFirst; // Last row in reference + } + if (!isset($colLast)) { + $colLast = $colFirst; // Last col in reference + } + + // Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) { + [$rwFirst, $rwLast] = [$rwLast, $rwFirst]; + } + + if ($colFirst > $colLast) { + [$colFirst, $colLast] = [$colLast, $colFirst]; + } + + $header = pack('vv', $record, $length); + $data = pack('CvvvvvvCC', $pnn, $rwAct, $colAct, $irefAct, $cref, $rwFirst, $rwLast, $colFirst, $colLast); + $this->append($header . $data); + } + + /** + * Store the MERGEDCELLS records for all ranges of merged cells. + */ + private function writeMergedCells(): void + { + $mergeCells = $this->phpSheet->getMergeCells(); + $countMergeCells = count($mergeCells); + + if ($countMergeCells == 0) { + return; + } + + // maximum allowed number of merged cells per record + $maxCountMergeCellsPerRecord = 1027; + + // record identifier + $record = 0x00E5; + + // counter for total number of merged cells treated so far by the writer + $i = 0; + + // counter for number of merged cells written in record currently being written + $j = 0; + + // initialize record data + $recordData = ''; + + // loop through the merged cells + foreach ($mergeCells as $mergeCell) { + ++$i; + ++$j; + + // extract the row and column indexes + $range = Coordinate::splitRange($mergeCell); + [$first, $last] = $range[0]; + [$firstColumn, $firstRow] = Coordinate::coordinateFromString($first); + [$lastColumn, $lastRow] = Coordinate::coordinateFromString($last); + + $recordData .= pack('vvvv', $firstRow - 1, $lastRow - 1, Coordinate::columnIndexFromString($firstColumn) - 1, Coordinate::columnIndexFromString($lastColumn) - 1); + + // flush record if we have reached limit for number of merged cells, or reached final merged cell + if ($j == $maxCountMergeCellsPerRecord || $i == $countMergeCells) { + $recordData = pack('v', $j) . $recordData; + $length = strlen($recordData); + $header = pack('vv', $record, $length); + $this->append($header . $recordData); + + // initialize for next record, if any + $recordData = ''; + $j = 0; + } + } + } + + /** + * Write SHEETLAYOUT record. + */ + private function writeSheetLayout(): void + { + if (!$this->phpSheet->isTabColorSet()) { + return; + } + + $recordData = pack( + 'vvVVVvv', + 0x0862, + 0x0000, // unused + 0x00000000, // unused + 0x00000000, // unused + 0x00000014, // size of record data + $this->colors[$this->phpSheet->getTabColor()->getRGB()], // color index + 0x0000 // unused + ); + + $length = strlen($recordData); + + $record = 0x0862; // Record identifier + $header = pack('vv', $record, $length); + $this->append($header . $recordData); + } + + /** + * Write SHEETPROTECTION. + */ + private function writeSheetProtection(): void + { + // record identifier + $record = 0x0867; + + // prepare options + $options = (int) !$this->phpSheet->getProtection()->getObjects() + | (int) !$this->phpSheet->getProtection()->getScenarios() << 1 + | (int) !$this->phpSheet->getProtection()->getFormatCells() << 2 + | (int) !$this->phpSheet->getProtection()->getFormatColumns() << 3 + | (int) !$this->phpSheet->getProtection()->getFormatRows() << 4 + | (int) !$this->phpSheet->getProtection()->getInsertColumns() << 5 + | (int) !$this->phpSheet->getProtection()->getInsertRows() << 6 + | (int) !$this->phpSheet->getProtection()->getInsertHyperlinks() << 7 + | (int) !$this->phpSheet->getProtection()->getDeleteColumns() << 8 + | (int) !$this->phpSheet->getProtection()->getDeleteRows() << 9 + | (int) !$this->phpSheet->getProtection()->getSelectLockedCells() << 10 + | (int) !$this->phpSheet->getProtection()->getSort() << 11 + | (int) !$this->phpSheet->getProtection()->getAutoFilter() << 12 + | (int) !$this->phpSheet->getProtection()->getPivotTables() << 13 + | (int) !$this->phpSheet->getProtection()->getSelectUnlockedCells() << 14; + + // record data + $recordData = pack( + 'vVVCVVvv', + 0x0867, // repeated record identifier + 0x0000, // not used + 0x0000, // not used + 0x00, // not used + 0x01000200, // unknown data + 0xFFFFFFFF, // unknown data + $options, // options + 0x0000 // not used + ); + + $length = strlen($recordData); + $header = pack('vv', $record, $length); + + $this->append($header . $recordData); + } + + /** + * Write BIFF record RANGEPROTECTION. + * + * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records + * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records + */ + private function writeRangeProtection(): void + { + foreach ($this->phpSheet->getProtectedCells() as $range => $password) { + // number of ranges, e.g. 'A1:B3 C20:D25' + $cellRanges = explode(' ', $range); + $cref = count($cellRanges); + + $recordData = pack( + 'vvVVvCVvVv', + 0x0868, + 0x00, + 0x0000, + 0x0000, + 0x02, + 0x0, + 0x0000, + $cref, + 0x0000, + 0x00 + ); + + foreach ($cellRanges as $cellRange) { + $recordData .= $this->writeBIFF8CellRangeAddressFixed($cellRange); + } + + // the rgbFeat structure + $recordData .= pack( + 'VV', + 0x0000, + hexdec($password) + ); + + $recordData .= StringHelper::UTF8toBIFF8UnicodeLong('p' . md5($recordData)); + + $length = strlen($recordData); + + $record = 0x0868; // Record identifier + $header = pack('vv', $record, $length); + $this->append($header . $recordData); + } + } + + /** + * Writes the Excel BIFF PANE record. + * The panes can either be frozen or thawed (unfrozen). + * Frozen panes are specified in terms of an integer number of rows and columns. + * Thawed panes are specified in terms of Excel's units for rows and columns. + */ + private function writePanes(): void + { + $panes = []; + if ($this->phpSheet->getFreezePane()) { + [$column, $row] = Coordinate::coordinateFromString($this->phpSheet->getFreezePane()); + $panes[0] = Coordinate::columnIndexFromString($column) - 1; + $panes[1] = $row - 1; + + [$leftMostColumn, $topRow] = Coordinate::coordinateFromString($this->phpSheet->getTopLeftCell()); + //Coordinates are zero-based in xls files + $panes[2] = $topRow - 1; + $panes[3] = Coordinate::columnIndexFromString($leftMostColumn) - 1; + } else { + // thaw panes + return; + } + + $x = $panes[0] ?? null; + $y = $panes[1] ?? null; + $rwTop = $panes[2] ?? null; + $colLeft = $panes[3] ?? null; + if (count($panes) > 4) { // if Active pane was received + $pnnAct = $panes[4]; + } else { + $pnnAct = null; + } + $record = 0x0041; // Record identifier + $length = 0x000A; // Number of bytes to follow + + // Code specific to frozen or thawed panes. + if ($this->phpSheet->getFreezePane()) { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = $y; + } + if (!isset($colLeft)) { + $colLeft = $x; + } + } else { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = 0; + } + if (!isset($colLeft)) { + $colLeft = 0; + } + + // Convert Excel's row and column units to the internal units. + // The default row height is 12.75 + // The default column width is 8.43 + // The following slope and intersection values were interpolated. + // + $y = 20 * $y + 255; + $x = 113.879 * $x + 390; + } + + // Determine which pane should be active. There is also the undocumented + // option to override this should it be necessary: may be removed later. + // + if (!isset($pnnAct)) { + if ($x != 0 && $y != 0) { + $pnnAct = 0; // Bottom right + } + if ($x != 0 && $y == 0) { + $pnnAct = 1; // Top right + } + if ($x == 0 && $y != 0) { + $pnnAct = 2; // Bottom left + } + if ($x == 0 && $y == 0) { + $pnnAct = 3; // Top left + } + } + + $this->activePane = $pnnAct; // Used in writeSelection + + $header = pack('vv', $record, $length); + $data = pack('vvvvv', $x, $y, $rwTop, $colLeft, $pnnAct); + $this->append($header . $data); + } + + /** + * Store the page setup SETUP BIFF record. + */ + private function writeSetup(): void + { + $record = 0x00A1; // Record identifier + $length = 0x0022; // Number of bytes to follow + + $iPaperSize = $this->phpSheet->getPageSetup()->getPaperSize(); // Paper size + + $iScale = $this->phpSheet->getPageSetup()->getScale() ? + $this->phpSheet->getPageSetup()->getScale() : 100; // Print scaling factor + + $iPageStart = 0x01; // Starting page number + $iFitWidth = (int) $this->phpSheet->getPageSetup()->getFitToWidth(); // Fit to number of pages wide + $iFitHeight = (int) $this->phpSheet->getPageSetup()->getFitToHeight(); // Fit to number of pages high + $grbit = 0x00; // Option flags + $iRes = 0x0258; // Print resolution + $iVRes = 0x0258; // Vertical print resolution + + $numHdr = $this->phpSheet->getPageMargins()->getHeader(); // Header Margin + + $numFtr = $this->phpSheet->getPageMargins()->getFooter(); // Footer Margin + $iCopies = 0x01; // Number of copies + + // Order of printing pages + $fLeftToRight = $this->phpSheet->getPageSetup()->getPageOrder() === PageSetup::PAGEORDER_DOWN_THEN_OVER + ? 0x1 : 0x0; + // Page orientation + $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) + ? 0x0 : 0x1; + + $fNoPls = 0x0; // Setup not read from printer + $fNoColor = 0x0; // Print black and white + $fDraft = 0x0; // Print draft quality + $fNotes = 0x0; // Print notes + $fNoOrient = 0x0; // Orientation not set + $fUsePage = 0x0; // Use custom starting page + + $grbit = $fLeftToRight; + $grbit |= $fLandscape << 1; + $grbit |= $fNoPls << 2; + $grbit |= $fNoColor << 3; + $grbit |= $fDraft << 4; + $grbit |= $fNotes << 5; + $grbit |= $fNoOrient << 6; + $grbit |= $fUsePage << 7; + + $numHdr = pack('d', $numHdr); + $numFtr = pack('d', $numFtr); + if (self::getByteOrder()) { // if it's Big Endian + $numHdr = strrev($numHdr); + $numFtr = strrev($numFtr); + } + + $header = pack('vv', $record, $length); + $data1 = pack('vvvvvvvv', $iPaperSize, $iScale, $iPageStart, $iFitWidth, $iFitHeight, $grbit, $iRes, $iVRes); + $data2 = $numHdr . $numFtr; + $data3 = pack('v', $iCopies); + $this->append($header . $data1 . $data2 . $data3); + } + + /** + * Store the header caption BIFF record. + */ + private function writeHeader(): void + { + $record = 0x0014; // Record identifier + + /* removing for now + // need to fix character count (multibyte!) + if (strlen($this->phpSheet->getHeaderFooter()->getOddHeader()) <= 255) { + $str = $this->phpSheet->getHeaderFooter()->getOddHeader(); // header string + } else { + $str = ''; + } + */ + + $recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddHeader()); + $length = strlen($recordData); + + $header = pack('vv', $record, $length); + + $this->append($header . $recordData); + } + + /** + * Store the footer caption BIFF record. + */ + private function writeFooter(): void + { + $record = 0x0015; // Record identifier + + /* removing for now + // need to fix character count (multibyte!) + if (strlen($this->phpSheet->getHeaderFooter()->getOddFooter()) <= 255) { + $str = $this->phpSheet->getHeaderFooter()->getOddFooter(); + } else { + $str = ''; + } + */ + + $recordData = StringHelper::UTF8toBIFF8UnicodeLong($this->phpSheet->getHeaderFooter()->getOddFooter()); + $length = strlen($recordData); + + $header = pack('vv', $record, $length); + + $this->append($header . $recordData); + } + + /** + * Store the horizontal centering HCENTER BIFF record. + */ + private function writeHcenter(): void + { + $record = 0x0083; // Record identifier + $length = 0x0002; // Bytes to follow + + $fHCenter = $this->phpSheet->getPageSetup()->getHorizontalCentered() ? 1 : 0; // Horizontal centering + + $header = pack('vv', $record, $length); + $data = pack('v', $fHCenter); + + $this->append($header . $data); + } + + /** + * Store the vertical centering VCENTER BIFF record. + */ + private function writeVcenter(): void + { + $record = 0x0084; // Record identifier + $length = 0x0002; // Bytes to follow + + $fVCenter = $this->phpSheet->getPageSetup()->getVerticalCentered() ? 1 : 0; // Horizontal centering + + $header = pack('vv', $record, $length); + $data = pack('v', $fVCenter); + $this->append($header . $data); + } + + /** + * Store the LEFTMARGIN BIFF record. + */ + private function writeMarginLeft(): void + { + $record = 0x0026; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->phpSheet->getPageMargins()->getLeft(); // Margin in inches + + $header = pack('vv', $record, $length); + $data = pack('d', $margin); + if (self::getByteOrder()) { // if it's Big Endian + $data = strrev($data); + } + + $this->append($header . $data); + } + + /** + * Store the RIGHTMARGIN BIFF record. + */ + private function writeMarginRight(): void + { + $record = 0x0027; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->phpSheet->getPageMargins()->getRight(); // Margin in inches + + $header = pack('vv', $record, $length); + $data = pack('d', $margin); + if (self::getByteOrder()) { // if it's Big Endian + $data = strrev($data); + } + + $this->append($header . $data); + } + + /** + * Store the TOPMARGIN BIFF record. + */ + private function writeMarginTop(): void + { + $record = 0x0028; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->phpSheet->getPageMargins()->getTop(); // Margin in inches + + $header = pack('vv', $record, $length); + $data = pack('d', $margin); + if (self::getByteOrder()) { // if it's Big Endian + $data = strrev($data); + } + + $this->append($header . $data); + } + + /** + * Store the BOTTOMMARGIN BIFF record. + */ + private function writeMarginBottom(): void + { + $record = 0x0029; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->phpSheet->getPageMargins()->getBottom(); // Margin in inches + + $header = pack('vv', $record, $length); + $data = pack('d', $margin); + if (self::getByteOrder()) { // if it's Big Endian + $data = strrev($data); + } + + $this->append($header . $data); + } + + /** + * Write the PRINTHEADERS BIFF record. + */ + private function writePrintHeaders(): void + { + $record = 0x002a; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintRwCol = $this->printHeaders; // Boolean flag + + $header = pack('vv', $record, $length); + $data = pack('v', $fPrintRwCol); + $this->append($header . $data); + } + + /** + * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the + * GRIDSET record. + */ + private function writePrintGridlines(): void + { + $record = 0x002b; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintGrid = $this->phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag + + $header = pack('vv', $record, $length); + $data = pack('v', $fPrintGrid); + $this->append($header . $data); + } + + /** + * Write the GRIDSET BIFF record. Must be used in conjunction with the + * PRINTGRIDLINES record. + */ + private function writeGridset(): void + { + $record = 0x0082; // Record identifier + $length = 0x0002; // Bytes to follow + + $fGridSet = !$this->phpSheet->getPrintGridlines(); // Boolean flag + + $header = pack('vv', $record, $length); + $data = pack('v', $fGridSet); + $this->append($header . $data); + } + + /** + * Write the AUTOFILTERINFO BIFF record. This is used to configure the number of autofilter select used in the sheet. + */ + private function writeAutoFilterInfo(): void + { + $record = 0x009D; // Record identifier + $length = 0x0002; // Bytes to follow + + $rangeBounds = Coordinate::rangeBoundaries($this->phpSheet->getAutoFilter()->getRange()); + $iNumFilters = 1 + $rangeBounds[1][0] - $rangeBounds[0][0]; + + $header = pack('vv', $record, $length); + $data = pack('v', $iNumFilters); + $this->append($header . $data); + } + + /** + * Write the GUTS BIFF record. This is used to configure the gutter margins + * where Excel outline symbols are displayed. The visibility of the gutters is + * controlled by a flag in WSBOOL. + * + * @see writeWsbool() + */ + private function writeGuts(): void + { + $record = 0x0080; // Record identifier + $length = 0x0008; // Bytes to follow + + $dxRwGut = 0x0000; // Size of row gutter + $dxColGut = 0x0000; // Size of col gutter + + // determine maximum row outline level + $maxRowOutlineLevel = 0; + foreach ($this->phpSheet->getRowDimensions() as $rowDimension) { + $maxRowOutlineLevel = max($maxRowOutlineLevel, $rowDimension->getOutlineLevel()); + } + + $col_level = 0; + + // Calculate the maximum column outline level. The equivalent calculation + // for the row outline level is carried out in writeRow(). + $colcount = count($this->columnInfo); + for ($i = 0; $i < $colcount; ++$i) { + $col_level = max($this->columnInfo[$i][5], $col_level); + } + + // Set the limits for the outline levels (0 <= x <= 7). + $col_level = max(0, min($col_level, 7)); + + // The displayed level is one greater than the max outline levels + if ($maxRowOutlineLevel) { + ++$maxRowOutlineLevel; + } + if ($col_level) { + ++$col_level; + } + + $header = pack('vv', $record, $length); + $data = pack('vvvv', $dxRwGut, $dxColGut, $maxRowOutlineLevel, $col_level); + + $this->append($header . $data); + } + + /** + * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction + * with the SETUP record. + */ + private function writeWsbool(): void + { + $record = 0x0081; // Record identifier + $length = 0x0002; // Bytes to follow + $grbit = 0x0000; + + // The only option that is of interest is the flag for fit to page. So we + // set all the options in one go. + // + // Set the option flags + $grbit |= 0x0001; // Auto page breaks visible + if ($this->outlineStyle) { + $grbit |= 0x0020; // Auto outline styles + } + if ($this->phpSheet->getShowSummaryBelow()) { + $grbit |= 0x0040; // Outline summary below + } + if ($this->phpSheet->getShowSummaryRight()) { + $grbit |= 0x0080; // Outline summary right + } + if ($this->phpSheet->getPageSetup()->getFitToPage()) { + $grbit |= 0x0100; // Page setup fit to page + } + if ($this->outlineOn) { + $grbit |= 0x0400; // Outline symbols displayed + } + + $header = pack('vv', $record, $length); + $data = pack('v', $grbit); + $this->append($header . $data); + } + + /** + * Write the HORIZONTALPAGEBREAKS and VERTICALPAGEBREAKS BIFF records. + */ + private function writeBreaks(): void + { + // initialize + $vbreaks = []; + $hbreaks = []; + + foreach ($this->phpSheet->getBreaks() as $cell => $breakType) { + // Fetch coordinates + $coordinates = Coordinate::coordinateFromString($cell); + + // Decide what to do by the type of break + switch ($breakType) { + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN: + // Add to list of vertical breaks + $vbreaks[] = Coordinate::columnIndexFromString($coordinates[0]) - 1; + + break; + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW: + // Add to list of horizontal breaks + $hbreaks[] = $coordinates[1]; + + break; + case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_NONE: + default: + // Nothing to do + break; + } + } + + //horizontal page breaks + if (!empty($hbreaks)) { + // Sort and filter array of page breaks + sort($hbreaks, SORT_NUMERIC); + if ($hbreaks[0] == 0) { // don't use first break if it's 0 + array_shift($hbreaks); + } + + $record = 0x001b; // Record identifier + $cbrk = count($hbreaks); // Number of page breaks + $length = 2 + 6 * $cbrk; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', $cbrk); + + // Append each page break + foreach ($hbreaks as $hbreak) { + $data .= pack('vvv', $hbreak, 0x0000, 0x00ff); + } + + $this->append($header . $data); + } + + // vertical page breaks + if (!empty($vbreaks)) { + // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. + // It is slightly higher in Excel 97/200, approx. 1026 + $vbreaks = array_slice($vbreaks, 0, 1000); + + // Sort and filter array of page breaks + sort($vbreaks, SORT_NUMERIC); + if ($vbreaks[0] == 0) { // don't use first break if it's 0 + array_shift($vbreaks); + } + + $record = 0x001a; // Record identifier + $cbrk = count($vbreaks); // Number of page breaks + $length = 2 + 6 * $cbrk; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', $cbrk); + + // Append each page break + foreach ($vbreaks as $vbreak) { + $data .= pack('vvv', $vbreak, 0x0000, 0xffff); + } + + $this->append($header . $data); + } + } + + /** + * Set the Biff PROTECT record to indicate that the worksheet is protected. + */ + private function writeProtect(): void + { + // Exit unless sheet protection has been specified + if (!$this->phpSheet->getProtection()->getSheet()) { + return; + } + + $record = 0x0012; // Record identifier + $length = 0x0002; // Bytes to follow + + $fLock = 1; // Worksheet is protected + + $header = pack('vv', $record, $length); + $data = pack('v', $fLock); + + $this->append($header . $data); + } + + /** + * Write SCENPROTECT. + */ + private function writeScenProtect(): void + { + // Exit if sheet protection is not active + if (!$this->phpSheet->getProtection()->getSheet()) { + return; + } + + // Exit if scenarios are not protected + if (!$this->phpSheet->getProtection()->getScenarios()) { + return; + } + + $record = 0x00DD; // Record identifier + $length = 0x0002; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', 1); + + $this->append($header . $data); + } + + /** + * Write OBJECTPROTECT. + */ + private function writeObjectProtect(): void + { + // Exit if sheet protection is not active + if (!$this->phpSheet->getProtection()->getSheet()) { + return; + } + + // Exit if objects are not protected + if (!$this->phpSheet->getProtection()->getObjects()) { + return; + } + + $record = 0x0063; // Record identifier + $length = 0x0002; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('v', 1); + + $this->append($header . $data); + } + + /** + * Write the worksheet PASSWORD record. + */ + private function writePassword(): void + { + // Exit unless sheet protection and password have been specified + if (!$this->phpSheet->getProtection()->getSheet() || !$this->phpSheet->getProtection()->getPassword()) { + return; + } + + $record = 0x0013; // Record identifier + $length = 0x0002; // Bytes to follow + + $wPassword = hexdec($this->phpSheet->getProtection()->getPassword()); // Encoded password + + $header = pack('vv', $record, $length); + $data = pack('v', $wPassword); + + $this->append($header . $data); + } + + /** + * Insert a 24bit bitmap image in a worksheet. + * + * @param int $row The row we are going to insert the bitmap into + * @param int $col The column we are going to insert the bitmap into + * @param mixed $bitmap The bitmap filename or GD-image resource + * @param int $x the horizontal position (offset) of the image inside the cell + * @param int $y the vertical position (offset) of the image inside the cell + * @param float $scale_x The horizontal scale + * @param float $scale_y The vertical scale + */ + public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void + { + $bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap)); + [$width, $height, $size, $data] = $bitmap_array; + + // Scale the frame of the image. + $width *= $scale_x; + $height *= $scale_y; + + // Calculate the vertices of the image and write the OBJ record + $this->positionImage($col, $row, $x, $y, $width, $height); + + // Write the IMDATA record to store the bitmap data + $record = 0x007f; + $length = 8 + $size; + $cf = 0x09; + $env = 0x01; + $lcb = $size; + + $header = pack('vvvvV', $record, $length, $cf, $env, $lcb); + $this->append($header . $data); + } + + /** + * Calculate the vertices that define the position of the image as required by + * the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be taken into + * account. + * The values of $col_start and $row_start are passed in from the calling + * function. The values of $col_end and $row_end are calculated by subtracting + * the width and height of the bitmap from the width and height of the + * underlying cells. + * The vertices are expressed as a percentage of the underlying cell width as + * follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * The SDK incorrectly states that the height should be expressed as a + * percentage of 1024. + * + * @param int $col_start Col containing upper left corner of object + * @param int $row_start Row containing top left corner of object + * @param int $x1 Distance to left side of object + * @param int $y1 Distance to top of object + * @param int $width Width of image frame + * @param int $height Height of image frame + */ + public function positionImage($col_start, $row_start, $x1, $y1, $width, $height): void + { + // Initialise end cell to the same as the start cell + $col_end = $col_start; // Col containing lower right corner of object + $row_end = $row_start; // Row containing bottom right corner of object + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1))) { + $x1 = 0; + } + if ($y1 >= Xls::sizeRow($this->phpSheet, $row_start + 1)) { + $y1 = 0; + } + + $width = $width + $x1 - 1; + $height = $height + $y1 - 1; + + // Subtract the underlying cell widths to find the end cell of the image + while ($width >= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1))) { + $width -= Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)); + ++$col_end; + } + + // Subtract the underlying cell heights to find the end cell of the image + while ($height >= Xls::sizeRow($this->phpSheet, $row_end + 1)) { + $height -= Xls::sizeRow($this->phpSheet, $row_end + 1); + ++$row_end; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + // with zero eight or width. + // + if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) == 0) { + return; + } + if (Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) == 0) { + return; + } + if (Xls::sizeRow($this->phpSheet, $row_start + 1) == 0) { + return; + } + if (Xls::sizeRow($this->phpSheet, $row_end + 1) == 0) { + return; + } + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_start + 1)) * 1024; + $y1 = $y1 / Xls::sizeRow($this->phpSheet, $row_start + 1) * 256; + $x2 = $width / Xls::sizeCol($this->phpSheet, Coordinate::stringFromColumnIndex($col_end + 1)) * 1024; // Distance to right side of object + $y2 = $height / Xls::sizeRow($this->phpSheet, $row_end + 1) * 256; // Distance to bottom of object + + $this->writeObjPicture($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2); + } + + /** + * Store the OBJ record that precedes an IMDATA record. This could be generalise + * to support other Excel objects. + * + * @param int $colL Column containing upper left corner of object + * @param int $dxL Distance from left side of cell + * @param int $rwT Row containing top left corner of object + * @param int $dyT Distance from top of cell + * @param int $colR Column containing lower right corner of object + * @param int $dxR Distance from right of cell + * @param int $rwB Row containing bottom right corner of object + * @param int $dyB Distance from bottom of cell + */ + private function writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB): void + { + $record = 0x005d; // Record identifier + $length = 0x003c; // Bytes to follow + + $cObj = 0x0001; // Count of objects in file (set to 1) + $OT = 0x0008; // Object type. 8 = Picture + $id = 0x0001; // Object ID + $grbit = 0x0614; // Option flags + + $cbMacro = 0x0000; // Length of FMLA structure + $Reserved1 = 0x0000; // Reserved + $Reserved2 = 0x0000; // Reserved + + $icvBack = 0x09; // Background colour + $icvFore = 0x09; // Foreground colour + $fls = 0x00; // Fill pattern + $fAuto = 0x00; // Automatic fill + $icv = 0x08; // Line colour + $lns = 0xff; // Line style + $lnw = 0x01; // Line weight + $fAutoB = 0x00; // Automatic border + $frs = 0x0000; // Frame style + $cf = 0x0009; // Image format, 9 = bitmap + $Reserved3 = 0x0000; // Reserved + $cbPictFmla = 0x0000; // Length of FMLA structure + $Reserved4 = 0x0000; // Reserved + $grbit2 = 0x0001; // Option flags + $Reserved5 = 0x0000; // Reserved + + $header = pack('vv', $record, $length); + $data = pack('V', $cObj); + $data .= pack('v', $OT); + $data .= pack('v', $id); + $data .= pack('v', $grbit); + $data .= pack('v', $colL); + $data .= pack('v', $dxL); + $data .= pack('v', $rwT); + $data .= pack('v', $dyT); + $data .= pack('v', $colR); + $data .= pack('v', $dxR); + $data .= pack('v', $rwB); + $data .= pack('v', $dyB); + $data .= pack('v', $cbMacro); + $data .= pack('V', $Reserved1); + $data .= pack('v', $Reserved2); + $data .= pack('C', $icvBack); + $data .= pack('C', $icvFore); + $data .= pack('C', $fls); + $data .= pack('C', $fAuto); + $data .= pack('C', $icv); + $data .= pack('C', $lns); + $data .= pack('C', $lnw); + $data .= pack('C', $fAutoB); + $data .= pack('v', $frs); + $data .= pack('V', $cf); + $data .= pack('v', $Reserved3); + $data .= pack('v', $cbPictFmla); + $data .= pack('v', $Reserved4); + $data .= pack('v', $grbit2); + $data .= pack('V', $Reserved5); + + $this->append($header . $data); + } + + /** + * Convert a GD-image into the internal format. + * + * @param GdImage|resource $image The image to process + * + * @return array Array with data and properties of the bitmap + */ + public function processBitmapGd($image) + { + $width = imagesx($image); + $height = imagesy($image); + + $data = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18); + for ($j = $height; --$j;) { + for ($i = 0; $i < $width; ++$i) { + $color = imagecolorsforindex($image, imagecolorat($image, $i, $j)); + foreach (['red', 'green', 'blue'] as $key) { + $color[$key] = $color[$key] + round((255 - $color[$key]) * $color['alpha'] / 127); + } + $data .= chr($color['blue']) . chr($color['green']) . chr($color['red']); + } + if (3 * $width % 4) { + $data .= str_repeat("\x00", 4 - 3 * $width % 4); + } + } + + return [$width, $height, strlen($data), $data]; + } + + /** + * Convert a 24 bit bitmap into the modified internal format used by Windows. + * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the + * MSDN library. + * + * @param string $bitmap The bitmap to process + * + * @return array Array with data and properties of the bitmap + */ + public function processBitmap($bitmap) + { + // Open file. + $bmp_fd = @fopen($bitmap, 'rb'); + if (!$bmp_fd) { + throw new WriterException("Couldn't import $bitmap"); + } + + // Slurp the file into a string. + $data = fread($bmp_fd, filesize($bitmap)); + + // Check that the file is big enough to be a bitmap. + if (strlen($data) <= 0x36) { + throw new WriterException("$bitmap doesn't contain enough data.\n"); + } + + // The first 2 bytes are used to identify the bitmap. + $identity = unpack('A2ident', $data); + if ($identity['ident'] != 'BM') { + throw new WriterException("$bitmap doesn't appear to be a valid bitmap image.\n"); + } + + // Remove bitmap data: ID. + $data = substr($data, 2); + + // Read and remove the bitmap size. This is more reliable than reading + // the data size at offset 0x22. + // + $size_array = unpack('Vsa', substr($data, 0, 4)); + $size = $size_array['sa']; + $data = substr($data, 4); + $size -= 0x36; // Subtract size of bitmap header. + $size += 0x0C; // Add size of BIFF header. + + // Remove bitmap data: reserved, offset, header length. + $data = substr($data, 12); + + // Read and remove the bitmap width and height. Verify the sizes. + $width_and_height = unpack('V2', substr($data, 0, 8)); + $width = $width_and_height[1]; + $height = $width_and_height[2]; + $data = substr($data, 8); + if ($width > 0xFFFF) { + throw new WriterException("$bitmap: largest image width supported is 65k.\n"); + } + if ($height > 0xFFFF) { + throw new WriterException("$bitmap: largest image height supported is 65k.\n"); + } + + // Read and remove the bitmap planes and bpp data. Verify them. + $planes_and_bitcount = unpack('v2', substr($data, 0, 4)); + $data = substr($data, 4); + if ($planes_and_bitcount[2] != 24) { // Bitcount + throw new WriterException("$bitmap isn't a 24bit true color bitmap.\n"); + } + if ($planes_and_bitcount[1] != 1) { + throw new WriterException("$bitmap: only 1 plane supported in bitmap image.\n"); + } + + // Read and remove the bitmap compression. Verify compression. + $compression = unpack('Vcomp', substr($data, 0, 4)); + $data = substr($data, 4); + + if ($compression['comp'] != 0) { + throw new WriterException("$bitmap: compression not supported in bitmap image.\n"); + } + + // Remove bitmap data: data size, hres, vres, colours, imp. colours. + $data = substr($data, 20); + + // Add the BITMAPCOREHEADER data + $header = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18); + $data = $header . $data; + + return [$width, $height, $size, $data]; + } + + /** + * Store the window zoom factor. This should be a reduced fraction but for + * simplicity we will store all fractions with a numerator of 100. + */ + private function writeZoom(): void + { + // If scale is 100 we don't need to write a record + if ($this->phpSheet->getSheetView()->getZoomScale() == 100) { + return; + } + + $record = 0x00A0; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack('vv', $record, $length); + $data = pack('vv', $this->phpSheet->getSheetView()->getZoomScale(), 100); + $this->append($header . $data); + } + + /** + * Get Escher object. + * + * @return \PhpOffice\PhpSpreadsheet\Shared\Escher + */ + public function getEscher() + { + return $this->escher; + } + + /** + * Set Escher object. + * + * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue + */ + public function setEscher(?\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null): void + { + $this->escher = $pValue; + } + + /** + * Write MSODRAWING record. + */ + private function writeMsoDrawing(): void + { + // write the Escher stream if necessary + if (isset($this->escher)) { + $writer = new Escher($this->escher); + $data = $writer->close(); + $spOffsets = $writer->getSpOffsets(); + $spTypes = $writer->getSpTypes(); + // write the neccesary MSODRAWING, OBJ records + + // split the Escher stream + $spOffsets[0] = 0; + $nm = count($spOffsets) - 1; // number of shapes excluding first shape + for ($i = 1; $i <= $nm; ++$i) { + // MSODRAWING record + $record = 0x00EC; // Record identifier + + // chunk of Escher stream for one shape + $dataChunk = substr($data, $spOffsets[$i - 1], $spOffsets[$i] - $spOffsets[$i - 1]); + + $length = strlen($dataChunk); + $header = pack('vv', $record, $length); + + $this->append($header . $dataChunk); + + // OBJ record + $record = 0x005D; // record identifier + $objData = ''; + + // ftCmo + if ($spTypes[$i] == 0x00C9) { + // Add ftCmo (common object data) subobject + $objData .= + pack( + 'vvvvvVVV', + 0x0015, // 0x0015 = ftCmo + 0x0012, // length of ftCmo data + 0x0014, // object type, 0x0014 = filter + $i, // object id number, Excel seems to use 1-based index, local for the sheet + 0x2101, // option flags, 0x2001 is what OpenOffice.org uses + 0, // reserved + 0, // reserved + 0 // reserved + ); + + // Add ftSbs Scroll bar subobject + $objData .= pack('vv', 0x00C, 0x0014); + $objData .= pack('H*', '0000000000000000640001000A00000010000100'); + // Add ftLbsData (List box data) subobject + $objData .= pack('vv', 0x0013, 0x1FEE); + $objData .= pack('H*', '00000000010001030000020008005700'); + } else { + // Add ftCmo (common object data) subobject + $objData .= + pack( + 'vvvvvVVV', + 0x0015, // 0x0015 = ftCmo + 0x0012, // length of ftCmo data + 0x0008, // object type, 0x0008 = picture + $i, // object id number, Excel seems to use 1-based index, local for the sheet + 0x6011, // option flags, 0x6011 is what OpenOffice.org uses + 0, // reserved + 0, // reserved + 0 // reserved + ); + } + + // ftEnd + $objData .= + pack( + 'vv', + 0x0000, // 0x0000 = ftEnd + 0x0000 // length of ftEnd data + ); + + $length = strlen($objData); + $header = pack('vv', $record, $length); + $this->append($header . $objData); + } + } + } + + /** + * Store the DATAVALIDATIONS and DATAVALIDATION records. + */ + private function writeDataValidity(): void + { + // Datavalidation collection + $dataValidationCollection = $this->phpSheet->getDataValidationCollection(); + + // Write data validations? + if (!empty($dataValidationCollection)) { + // DATAVALIDATIONS record + $record = 0x01B2; // Record identifier + $length = 0x0012; // Bytes to follow + + $grbit = 0x0000; // Prompt box at cell, no cached validity data at DV records + $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position + $verPos = 0x00000000; // Vertical position of prompt box, if fixed position + $objId = 0xFFFFFFFF; // Object identifier of drop down arrow object, or -1 if not visible + + $header = pack('vv', $record, $length); + $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, count($dataValidationCollection)); + $this->append($header . $data); + + // DATAVALIDATION records + $record = 0x01BE; // Record identifier + + foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) { + // initialize record data + $data = ''; + + // options + $options = 0x00000000; + + // data type + $type = 0x00; + switch ($dataValidation->getType()) { + case DataValidation::TYPE_NONE: + $type = 0x00; + + break; + case DataValidation::TYPE_WHOLE: + $type = 0x01; + + break; + case DataValidation::TYPE_DECIMAL: + $type = 0x02; + + break; + case DataValidation::TYPE_LIST: + $type = 0x03; + + break; + case DataValidation::TYPE_DATE: + $type = 0x04; + + break; + case DataValidation::TYPE_TIME: + $type = 0x05; + + break; + case DataValidation::TYPE_TEXTLENGTH: + $type = 0x06; + + break; + case DataValidation::TYPE_CUSTOM: + $type = 0x07; + + break; + } + + $options |= $type << 0; + + // error style + $errorStyle = 0x00; + switch ($dataValidation->getErrorStyle()) { + case DataValidation::STYLE_STOP: + $errorStyle = 0x00; + + break; + case DataValidation::STYLE_WARNING: + $errorStyle = 0x01; + + break; + case DataValidation::STYLE_INFORMATION: + $errorStyle = 0x02; + + break; + } + + $options |= $errorStyle << 4; + + // explicit formula? + if ($type == 0x03 && preg_match('/^\".*\"$/', $dataValidation->getFormula1())) { + $options |= 0x01 << 7; + } + + // empty cells allowed + $options |= $dataValidation->getAllowBlank() << 8; + + // show drop down + $options |= (!$dataValidation->getShowDropDown()) << 9; + + // show input message + $options |= $dataValidation->getShowInputMessage() << 18; + + // show error message + $options |= $dataValidation->getShowErrorMessage() << 19; + + // condition operator + $operator = 0x00; + switch ($dataValidation->getOperator()) { + case DataValidation::OPERATOR_BETWEEN: + $operator = 0x00; + + break; + case DataValidation::OPERATOR_NOTBETWEEN: + $operator = 0x01; + + break; + case DataValidation::OPERATOR_EQUAL: + $operator = 0x02; + + break; + case DataValidation::OPERATOR_NOTEQUAL: + $operator = 0x03; + + break; + case DataValidation::OPERATOR_GREATERTHAN: + $operator = 0x04; + + break; + case DataValidation::OPERATOR_LESSTHAN: + $operator = 0x05; + + break; + case DataValidation::OPERATOR_GREATERTHANOREQUAL: + $operator = 0x06; + + break; + case DataValidation::OPERATOR_LESSTHANOREQUAL: + $operator = 0x07; + + break; + } + + $options |= $operator << 20; + + $data = pack('V', $options); + + // prompt title + $promptTitle = $dataValidation->getPromptTitle() !== '' ? + $dataValidation->getPromptTitle() : chr(0); + $data .= StringHelper::UTF8toBIFF8UnicodeLong($promptTitle); + + // error title + $errorTitle = $dataValidation->getErrorTitle() !== '' ? + $dataValidation->getErrorTitle() : chr(0); + $data .= StringHelper::UTF8toBIFF8UnicodeLong($errorTitle); + + // prompt text + $prompt = $dataValidation->getPrompt() !== '' ? + $dataValidation->getPrompt() : chr(0); + $data .= StringHelper::UTF8toBIFF8UnicodeLong($prompt); + + // error text + $error = $dataValidation->getError() !== '' ? + $dataValidation->getError() : chr(0); + $data .= StringHelper::UTF8toBIFF8UnicodeLong($error); + + // formula 1 + try { + $formula1 = $dataValidation->getFormula1(); + if ($type == 0x03) { // list type + $formula1 = str_replace(',', chr(0), $formula1); + } + $this->parser->parse($formula1); + $formula1 = $this->parser->toReversePolish(); + $sz1 = strlen($formula1); + } catch (PhpSpreadsheetException $e) { + $sz1 = 0; + $formula1 = ''; + } + $data .= pack('vv', $sz1, 0x0000); + $data .= $formula1; + + // formula 2 + try { + $formula2 = $dataValidation->getFormula2(); + if ($formula2 === '') { + throw new WriterException('No formula2'); + } + $this->parser->parse($formula2); + $formula2 = $this->parser->toReversePolish(); + $sz2 = strlen($formula2); + } catch (PhpSpreadsheetException $e) { + $sz2 = 0; + $formula2 = ''; + } + $data .= pack('vv', $sz2, 0x0000); + $data .= $formula2; + + // cell range address list + $data .= pack('v', 0x0001); + $data .= $this->writeBIFF8CellRangeAddressFixed($cellCoordinate); + + $length = strlen($data); + $header = pack('vv', $record, $length); + + $this->append($header . $data); + } + } + } + + /** + * Map Error code. + * + * @param string $errorCode + * + * @return int + */ + private static function mapErrorCode($errorCode) + { + switch ($errorCode) { + case '#NULL!': + return 0x00; + case '#DIV/0!': + return 0x07; + case '#VALUE!': + return 0x0F; + case '#REF!': + return 0x17; + case '#NAME?': + return 0x1D; + case '#NUM!': + return 0x24; + case '#N/A': + return 0x2A; + } + + return 0; + } + + /** + * Write PLV Record. + */ + private function writePageLayoutView(): void + { + $record = 0x088B; // Record identifier + $length = 0x0010; // Bytes to follow + + $rt = 0x088B; // 2 + $grbitFrt = 0x0000; // 2 + $reserved = 0x0000000000000000; // 8 + $wScalvePLV = $this->phpSheet->getSheetView()->getZoomScale(); // 2 + + // The options flags that comprise $grbit + if ($this->phpSheet->getSheetView()->getView() == SheetView::SHEETVIEW_PAGE_LAYOUT) { + $fPageLayoutView = 1; + } else { + $fPageLayoutView = 0; + } + $fRulerVisible = 0; + $fWhitespaceHidden = 0; + + $grbit = $fPageLayoutView; // 2 + $grbit |= $fRulerVisible << 1; + $grbit |= $fWhitespaceHidden << 3; + + $header = pack('vv', $record, $length); + $data = pack('vvVVvv', $rt, $grbitFrt, 0x00000000, 0x00000000, $wScalvePLV, $grbit); + $this->append($header . $data); + } + + /** + * Write CFRule Record. + */ + private function writeCFRule(Conditional $conditional): void + { + $record = 0x01B1; // Record identifier + + // $type : Type of the CF + // $operatorType : Comparison operator + if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) { + $type = 0x02; + $operatorType = 0x00; + } elseif ($conditional->getConditionType() == Conditional::CONDITION_CELLIS) { + $type = 0x01; + + switch ($conditional->getOperatorType()) { + case Conditional::OPERATOR_NONE: + $operatorType = 0x00; + + break; + case Conditional::OPERATOR_EQUAL: + $operatorType = 0x03; + + break; + case Conditional::OPERATOR_GREATERTHAN: + $operatorType = 0x05; + + break; + case Conditional::OPERATOR_GREATERTHANOREQUAL: + $operatorType = 0x07; + + break; + case Conditional::OPERATOR_LESSTHAN: + $operatorType = 0x06; + + break; + case Conditional::OPERATOR_LESSTHANOREQUAL: + $operatorType = 0x08; + + break; + case Conditional::OPERATOR_NOTEQUAL: + $operatorType = 0x04; + + break; + case Conditional::OPERATOR_BETWEEN: + $operatorType = 0x01; + + break; + // not OPERATOR_NOTBETWEEN 0x02 + } + } + + // $szValue1 : size of the formula data for first value or formula + // $szValue2 : size of the formula data for second value or formula + $arrConditions = $conditional->getConditions(); + $numConditions = count($arrConditions); + if ($numConditions == 1) { + $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000); + $szValue2 = 0x0000; + $operand1 = pack('Cv', 0x1E, $arrConditions[0]); + $operand2 = null; + } elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) { + $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000); + $szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000); + $operand1 = pack('Cv', 0x1E, $arrConditions[0]); + $operand2 = pack('Cv', 0x1E, $arrConditions[1]); + } else { + $szValue1 = 0x0000; + $szValue2 = 0x0000; + $operand1 = null; + $operand2 = null; + } + + // $flags : Option flags + // Alignment + $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0); + $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0); + $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0); + $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0); + $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0); + $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0); + if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) { + $bFormatAlign = 1; + } else { + $bFormatAlign = 0; + } + // Protection + $bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0); + $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0); + if ($bProtLocked == 0 || $bProtHidden == 0) { + $bFormatProt = 1; + } else { + $bFormatProt = 0; + } + // Border + $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == Color::COLOR_BLACK + && $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); + $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == Color::COLOR_BLACK + && $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); + $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == Color::COLOR_BLACK + && $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); + $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == Color::COLOR_BLACK + && $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0); + if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) { + $bFormatBorder = 1; + } else { + $bFormatBorder = 0; + } + // Pattern + $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1); + $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1); + $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1); + if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) { + $bFormatFill = 1; + } else { + $bFormatFill = 0; + } + // Font + if ( + $conditional->getStyle()->getFont()->getName() != null + || $conditional->getStyle()->getFont()->getSize() != null + || $conditional->getStyle()->getFont()->getBold() != null + || $conditional->getStyle()->getFont()->getItalic() != null + || $conditional->getStyle()->getFont()->getSuperscript() != null + || $conditional->getStyle()->getFont()->getSubscript() != null + || $conditional->getStyle()->getFont()->getUnderline() != null + || $conditional->getStyle()->getFont()->getStrikethrough() != null + || $conditional->getStyle()->getFont()->getColor()->getARGB() != null + ) { + $bFormatFont = 1; + } else { + $bFormatFont = 0; + } + // Alignment + $flags = 0; + $flags |= (1 == $bAlignHz ? 0x00000001 : 0); + $flags |= (1 == $bAlignVt ? 0x00000002 : 0); + $flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0); + $flags |= (1 == $bTxRotation ? 0x00000008 : 0); + // Justify last line flag + $flags |= (1 == 1 ? 0x00000010 : 0); + $flags |= (1 == $bIndent ? 0x00000020 : 0); + $flags |= (1 == $bShrinkToFit ? 0x00000040 : 0); + // Default + $flags |= (1 == 1 ? 0x00000080 : 0); + // Protection + $flags |= (1 == $bProtLocked ? 0x00000100 : 0); + $flags |= (1 == $bProtHidden ? 0x00000200 : 0); + // Border + $flags |= (1 == $bBorderLeft ? 0x00000400 : 0); + $flags |= (1 == $bBorderRight ? 0x00000800 : 0); + $flags |= (1 == $bBorderTop ? 0x00001000 : 0); + $flags |= (1 == $bBorderBottom ? 0x00002000 : 0); + $flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border + $flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border + // Pattern + $flags |= (1 == $bFillStyle ? 0x00010000 : 0); + $flags |= (1 == $bFillColor ? 0x00020000 : 0); + $flags |= (1 == $bFillColorBg ? 0x00040000 : 0); + $flags |= (1 == 1 ? 0x00380000 : 0); + // Font + $flags |= (1 == $bFormatFont ? 0x04000000 : 0); + // Alignment: + $flags |= (1 == $bFormatAlign ? 0x08000000 : 0); + // Border + $flags |= (1 == $bFormatBorder ? 0x10000000 : 0); + // Pattern + $flags |= (1 == $bFormatFill ? 0x20000000 : 0); + // Protection + $flags |= (1 == $bFormatProt ? 0x40000000 : 0); + // Text direction + $flags |= (1 == 0 ? 0x80000000 : 0); + + // Data Blocks + if ($bFormatFont == 1) { + // Font Name + if ($conditional->getStyle()->getFont()->getName() == null) { + $dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); + $dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); + } else { + $dataBlockFont = StringHelper::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName()); + } + // Font Size + if ($conditional->getStyle()->getFont()->getSize() == null) { + $dataBlockFont .= pack('V', 20 * 11); + } else { + $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize()); + } + // Font Options + $dataBlockFont .= pack('V', 0); + // Font weight + if ($conditional->getStyle()->getFont()->getBold() == true) { + $dataBlockFont .= pack('v', 0x02BC); + } else { + $dataBlockFont .= pack('v', 0x0190); + } + // Escapement type + if ($conditional->getStyle()->getFont()->getSubscript() == true) { + $dataBlockFont .= pack('v', 0x02); + $fontEscapement = 0; + } elseif ($conditional->getStyle()->getFont()->getSuperscript() == true) { + $dataBlockFont .= pack('v', 0x01); + $fontEscapement = 0; + } else { + $dataBlockFont .= pack('v', 0x00); + $fontEscapement = 1; + } + // Underline type + switch ($conditional->getStyle()->getFont()->getUnderline()) { + case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE: + $dataBlockFont .= pack('C', 0x00); + $fontUnderline = 0; + + break; + case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE: + $dataBlockFont .= pack('C', 0x02); + $fontUnderline = 0; + + break; + case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING: + $dataBlockFont .= pack('C', 0x22); + $fontUnderline = 0; + + break; + case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE: + $dataBlockFont .= pack('C', 0x01); + $fontUnderline = 0; + + break; + case \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING: + $dataBlockFont .= pack('C', 0x21); + $fontUnderline = 0; + + break; + default: + $dataBlockFont .= pack('C', 0x00); + $fontUnderline = 1; + + break; + } + // Not used (3) + $dataBlockFont .= pack('vC', 0x0000, 0x00); + // Font color index + switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) { + case '000000': + $colorIdx = 0x08; + + break; + case 'FFFFFF': + $colorIdx = 0x09; + + break; + case 'FF0000': + $colorIdx = 0x0A; + + break; + case '00FF00': + $colorIdx = 0x0B; + + break; + case '0000FF': + $colorIdx = 0x0C; + + break; + case 'FFFF00': + $colorIdx = 0x0D; + + break; + case 'FF00FF': + $colorIdx = 0x0E; + + break; + case '00FFFF': + $colorIdx = 0x0F; + + break; + case '800000': + $colorIdx = 0x10; + + break; + case '008000': + $colorIdx = 0x11; + + break; + case '000080': + $colorIdx = 0x12; + + break; + case '808000': + $colorIdx = 0x13; + + break; + case '800080': + $colorIdx = 0x14; + + break; + case '008080': + $colorIdx = 0x15; + + break; + case 'C0C0C0': + $colorIdx = 0x16; + + break; + case '808080': + $colorIdx = 0x17; + + break; + case '9999FF': + $colorIdx = 0x18; + + break; + case '993366': + $colorIdx = 0x19; + + break; + case 'FFFFCC': + $colorIdx = 0x1A; + + break; + case 'CCFFFF': + $colorIdx = 0x1B; + + break; + case '660066': + $colorIdx = 0x1C; + + break; + case 'FF8080': + $colorIdx = 0x1D; + + break; + case '0066CC': + $colorIdx = 0x1E; + + break; + case 'CCCCFF': + $colorIdx = 0x1F; + + break; + case '000080': + $colorIdx = 0x20; + + break; + case 'FF00FF': + $colorIdx = 0x21; + + break; + case 'FFFF00': + $colorIdx = 0x22; + + break; + case '00FFFF': + $colorIdx = 0x23; + + break; + case '800080': + $colorIdx = 0x24; + + break; + case '800000': + $colorIdx = 0x25; + + break; + case '008080': + $colorIdx = 0x26; + + break; + case '0000FF': + $colorIdx = 0x27; + + break; + case '00CCFF': + $colorIdx = 0x28; + + break; + case 'CCFFFF': + $colorIdx = 0x29; + + break; + case 'CCFFCC': + $colorIdx = 0x2A; + + break; + case 'FFFF99': + $colorIdx = 0x2B; + + break; + case '99CCFF': + $colorIdx = 0x2C; + + break; + case 'FF99CC': + $colorIdx = 0x2D; + + break; + case 'CC99FF': + $colorIdx = 0x2E; + + break; + case 'FFCC99': + $colorIdx = 0x2F; + + break; + case '3366FF': + $colorIdx = 0x30; + + break; + case '33CCCC': + $colorIdx = 0x31; + + break; + case '99CC00': + $colorIdx = 0x32; + + break; + case 'FFCC00': + $colorIdx = 0x33; + + break; + case 'FF9900': + $colorIdx = 0x34; + + break; + case 'FF6600': + $colorIdx = 0x35; + + break; + case '666699': + $colorIdx = 0x36; + + break; + case '969696': + $colorIdx = 0x37; + + break; + case '003366': + $colorIdx = 0x38; + + break; + case '339966': + $colorIdx = 0x39; + + break; + case '003300': + $colorIdx = 0x3A; + + break; + case '333300': + $colorIdx = 0x3B; + + break; + case '993300': + $colorIdx = 0x3C; + + break; + case '993366': + $colorIdx = 0x3D; + + break; + case '333399': + $colorIdx = 0x3E; + + break; + case '333333': + $colorIdx = 0x3F; + + break; + default: + $colorIdx = 0x00; + + break; + } + $dataBlockFont .= pack('V', $colorIdx); + // Not used (4) + $dataBlockFont .= pack('V', 0x00000000); + // Options flags for modified font attributes + $optionsFlags = 0; + $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0); + $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0); + $optionsFlags |= (1 == 1 ? 0x00000008 : 0); + $optionsFlags |= (1 == 1 ? 0x00000010 : 0); + $optionsFlags |= (1 == 0 ? 0x00000020 : 0); + $optionsFlags |= (1 == 1 ? 0x00000080 : 0); + $dataBlockFont .= pack('V', $optionsFlags); + // Escapement type + $dataBlockFont .= pack('V', $fontEscapement); + // Underline type + $dataBlockFont .= pack('V', $fontUnderline); + // Always + $dataBlockFont .= pack('V', 0x00000000); + // Always + $dataBlockFont .= pack('V', 0x00000000); + // Not used (8) + $dataBlockFont .= pack('VV', 0x00000000, 0x00000000); + // Always + $dataBlockFont .= pack('v', 0x0001); + } + if ($bFormatAlign == 1) { + $blockAlign = 0; + // Alignment and text break + switch ($conditional->getStyle()->getAlignment()->getHorizontal()) { + case Alignment::HORIZONTAL_GENERAL: + $blockAlign = 0; + + break; + case Alignment::HORIZONTAL_LEFT: + $blockAlign = 1; + + break; + case Alignment::HORIZONTAL_RIGHT: + $blockAlign = 3; + + break; + case Alignment::HORIZONTAL_CENTER: + $blockAlign = 2; + + break; + case Alignment::HORIZONTAL_CENTER_CONTINUOUS: + $blockAlign = 6; + + break; + case Alignment::HORIZONTAL_JUSTIFY: + $blockAlign = 5; + + break; + } + if ($conditional->getStyle()->getAlignment()->getWrapText() == true) { + $blockAlign |= 1 << 3; + } else { + $blockAlign |= 0 << 3; + } + switch ($conditional->getStyle()->getAlignment()->getVertical()) { + case Alignment::VERTICAL_BOTTOM: + $blockAlign = 2 << 4; + + break; + case Alignment::VERTICAL_TOP: + $blockAlign = 0 << 4; + + break; + case Alignment::VERTICAL_CENTER: + $blockAlign = 1 << 4; + + break; + case Alignment::VERTICAL_JUSTIFY: + $blockAlign = 3 << 4; + + break; + } + $blockAlign |= 0 << 7; + + // Text rotation angle + $blockRotation = $conditional->getStyle()->getAlignment()->getTextRotation(); + + // Indentation + $blockIndent = $conditional->getStyle()->getAlignment()->getIndent(); + if ($conditional->getStyle()->getAlignment()->getShrinkToFit() == true) { + $blockIndent |= 1 << 4; + } else { + $blockIndent |= 0 << 4; + } + $blockIndent |= 0 << 6; + + // Relative indentation + $blockIndentRelative = 255; + + $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000); + } + if ($bFormatBorder == 1) { + $blockLineStyle = 0; + switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()) { + case Border::BORDER_NONE: + $blockLineStyle |= 0x00; + + break; + case Border::BORDER_THIN: + $blockLineStyle |= 0x01; + + break; + case Border::BORDER_MEDIUM: + $blockLineStyle |= 0x02; + + break; + case Border::BORDER_DASHED: + $blockLineStyle |= 0x03; + + break; + case Border::BORDER_DOTTED: + $blockLineStyle |= 0x04; + + break; + case Border::BORDER_THICK: + $blockLineStyle |= 0x05; + + break; + case Border::BORDER_DOUBLE: + $blockLineStyle |= 0x06; + + break; + case Border::BORDER_HAIR: + $blockLineStyle |= 0x07; + + break; + case Border::BORDER_MEDIUMDASHED: + $blockLineStyle |= 0x08; + + break; + case Border::BORDER_DASHDOT: + $blockLineStyle |= 0x09; + + break; + case Border::BORDER_MEDIUMDASHDOT: + $blockLineStyle |= 0x0A; + + break; + case Border::BORDER_DASHDOTDOT: + $blockLineStyle |= 0x0B; + + break; + case Border::BORDER_MEDIUMDASHDOTDOT: + $blockLineStyle |= 0x0C; + + break; + case Border::BORDER_SLANTDASHDOT: + $blockLineStyle |= 0x0D; + + break; + } + switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()) { + case Border::BORDER_NONE: + $blockLineStyle |= 0x00 << 4; + + break; + case Border::BORDER_THIN: + $blockLineStyle |= 0x01 << 4; + + break; + case Border::BORDER_MEDIUM: + $blockLineStyle |= 0x02 << 4; + + break; + case Border::BORDER_DASHED: + $blockLineStyle |= 0x03 << 4; + + break; + case Border::BORDER_DOTTED: + $blockLineStyle |= 0x04 << 4; + + break; + case Border::BORDER_THICK: + $blockLineStyle |= 0x05 << 4; + + break; + case Border::BORDER_DOUBLE: + $blockLineStyle |= 0x06 << 4; + + break; + case Border::BORDER_HAIR: + $blockLineStyle |= 0x07 << 4; + + break; + case Border::BORDER_MEDIUMDASHED: + $blockLineStyle |= 0x08 << 4; + + break; + case Border::BORDER_DASHDOT: + $blockLineStyle |= 0x09 << 4; + + break; + case Border::BORDER_MEDIUMDASHDOT: + $blockLineStyle |= 0x0A << 4; + + break; + case Border::BORDER_DASHDOTDOT: + $blockLineStyle |= 0x0B << 4; + + break; + case Border::BORDER_MEDIUMDASHDOTDOT: + $blockLineStyle |= 0x0C << 4; + + break; + case Border::BORDER_SLANTDASHDOT: + $blockLineStyle |= 0x0D << 4; + + break; + } + switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()) { + case Border::BORDER_NONE: + $blockLineStyle |= 0x00 << 8; + + break; + case Border::BORDER_THIN: + $blockLineStyle |= 0x01 << 8; + + break; + case Border::BORDER_MEDIUM: + $blockLineStyle |= 0x02 << 8; + + break; + case Border::BORDER_DASHED: + $blockLineStyle |= 0x03 << 8; + + break; + case Border::BORDER_DOTTED: + $blockLineStyle |= 0x04 << 8; + + break; + case Border::BORDER_THICK: + $blockLineStyle |= 0x05 << 8; + + break; + case Border::BORDER_DOUBLE: + $blockLineStyle |= 0x06 << 8; + + break; + case Border::BORDER_HAIR: + $blockLineStyle |= 0x07 << 8; + + break; + case Border::BORDER_MEDIUMDASHED: + $blockLineStyle |= 0x08 << 8; + + break; + case Border::BORDER_DASHDOT: + $blockLineStyle |= 0x09 << 8; + + break; + case Border::BORDER_MEDIUMDASHDOT: + $blockLineStyle |= 0x0A << 8; + + break; + case Border::BORDER_DASHDOTDOT: + $blockLineStyle |= 0x0B << 8; + + break; + case Border::BORDER_MEDIUMDASHDOTDOT: + $blockLineStyle |= 0x0C << 8; + + break; + case Border::BORDER_SLANTDASHDOT: + $blockLineStyle |= 0x0D << 8; + + break; + } + switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()) { + case Border::BORDER_NONE: + $blockLineStyle |= 0x00 << 12; + + break; + case Border::BORDER_THIN: + $blockLineStyle |= 0x01 << 12; + + break; + case Border::BORDER_MEDIUM: + $blockLineStyle |= 0x02 << 12; + + break; + case Border::BORDER_DASHED: + $blockLineStyle |= 0x03 << 12; + + break; + case Border::BORDER_DOTTED: + $blockLineStyle |= 0x04 << 12; + + break; + case Border::BORDER_THICK: + $blockLineStyle |= 0x05 << 12; + + break; + case Border::BORDER_DOUBLE: + $blockLineStyle |= 0x06 << 12; + + break; + case Border::BORDER_HAIR: + $blockLineStyle |= 0x07 << 12; + + break; + case Border::BORDER_MEDIUMDASHED: + $blockLineStyle |= 0x08 << 12; + + break; + case Border::BORDER_DASHDOT: + $blockLineStyle |= 0x09 << 12; + + break; + case Border::BORDER_MEDIUMDASHDOT: + $blockLineStyle |= 0x0A << 12; + + break; + case Border::BORDER_DASHDOTDOT: + $blockLineStyle |= 0x0B << 12; + + break; + case Border::BORDER_MEDIUMDASHDOTDOT: + $blockLineStyle |= 0x0C << 12; + + break; + case Border::BORDER_SLANTDASHDOT: + $blockLineStyle |= 0x0D << 12; + + break; + } + + // TODO writeCFRule() => $blockLineStyle => Index Color for left line + // TODO writeCFRule() => $blockLineStyle => Index Color for right line + // TODO writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off + // TODO writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off + $blockColor = 0; + // TODO writeCFRule() => $blockColor => Index Color for top line + // TODO writeCFRule() => $blockColor => Index Color for bottom line + // TODO writeCFRule() => $blockColor => Index Color for diagonal line + switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()) { + case Border::BORDER_NONE: + $blockColor |= 0x00 << 21; + + break; + case Border::BORDER_THIN: + $blockColor |= 0x01 << 21; + + break; + case Border::BORDER_MEDIUM: + $blockColor |= 0x02 << 21; + + break; + case Border::BORDER_DASHED: + $blockColor |= 0x03 << 21; + + break; + case Border::BORDER_DOTTED: + $blockColor |= 0x04 << 21; + + break; + case Border::BORDER_THICK: + $blockColor |= 0x05 << 21; + + break; + case Border::BORDER_DOUBLE: + $blockColor |= 0x06 << 21; + + break; + case Border::BORDER_HAIR: + $blockColor |= 0x07 << 21; + + break; + case Border::BORDER_MEDIUMDASHED: + $blockColor |= 0x08 << 21; + + break; + case Border::BORDER_DASHDOT: + $blockColor |= 0x09 << 21; + + break; + case Border::BORDER_MEDIUMDASHDOT: + $blockColor |= 0x0A << 21; + + break; + case Border::BORDER_DASHDOTDOT: + $blockColor |= 0x0B << 21; + + break; + case Border::BORDER_MEDIUMDASHDOTDOT: + $blockColor |= 0x0C << 21; + + break; + case Border::BORDER_SLANTDASHDOT: + $blockColor |= 0x0D << 21; + + break; + } + $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor); + } + if ($bFormatFill == 1) { + // Fill Patern Style + $blockFillPatternStyle = 0; + switch ($conditional->getStyle()->getFill()->getFillType()) { + case Fill::FILL_NONE: + $blockFillPatternStyle = 0x00; + + break; + case Fill::FILL_SOLID: + $blockFillPatternStyle = 0x01; + + break; + case Fill::FILL_PATTERN_MEDIUMGRAY: + $blockFillPatternStyle = 0x02; + + break; + case Fill::FILL_PATTERN_DARKGRAY: + $blockFillPatternStyle = 0x03; + + break; + case Fill::FILL_PATTERN_LIGHTGRAY: + $blockFillPatternStyle = 0x04; + + break; + case Fill::FILL_PATTERN_DARKHORIZONTAL: + $blockFillPatternStyle = 0x05; + + break; + case Fill::FILL_PATTERN_DARKVERTICAL: + $blockFillPatternStyle = 0x06; + + break; + case Fill::FILL_PATTERN_DARKDOWN: + $blockFillPatternStyle = 0x07; + + break; + case Fill::FILL_PATTERN_DARKUP: + $blockFillPatternStyle = 0x08; + + break; + case Fill::FILL_PATTERN_DARKGRID: + $blockFillPatternStyle = 0x09; + + break; + case Fill::FILL_PATTERN_DARKTRELLIS: + $blockFillPatternStyle = 0x0A; + + break; + case Fill::FILL_PATTERN_LIGHTHORIZONTAL: + $blockFillPatternStyle = 0x0B; + + break; + case Fill::FILL_PATTERN_LIGHTVERTICAL: + $blockFillPatternStyle = 0x0C; + + break; + case Fill::FILL_PATTERN_LIGHTDOWN: + $blockFillPatternStyle = 0x0D; + + break; + case Fill::FILL_PATTERN_LIGHTUP: + $blockFillPatternStyle = 0x0E; + + break; + case Fill::FILL_PATTERN_LIGHTGRID: + $blockFillPatternStyle = 0x0F; + + break; + case Fill::FILL_PATTERN_LIGHTTRELLIS: + $blockFillPatternStyle = 0x10; + + break; + case Fill::FILL_PATTERN_GRAY125: + $blockFillPatternStyle = 0x11; + + break; + case Fill::FILL_PATTERN_GRAY0625: + $blockFillPatternStyle = 0x12; + + break; + case Fill::FILL_GRADIENT_LINEAR: + $blockFillPatternStyle = 0x00; + + break; // does not exist in BIFF8 + case Fill::FILL_GRADIENT_PATH: + $blockFillPatternStyle = 0x00; + + break; // does not exist in BIFF8 + default: + $blockFillPatternStyle = 0x00; + + break; + } + // Color + switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) { + case '000000': + $colorIdxBg = 0x08; + + break; + case 'FFFFFF': + $colorIdxBg = 0x09; + + break; + case 'FF0000': + $colorIdxBg = 0x0A; + + break; + case '00FF00': + $colorIdxBg = 0x0B; + + break; + case '0000FF': + $colorIdxBg = 0x0C; + + break; + case 'FFFF00': + $colorIdxBg = 0x0D; + + break; + case 'FF00FF': + $colorIdxBg = 0x0E; + + break; + case '00FFFF': + $colorIdxBg = 0x0F; + + break; + case '800000': + $colorIdxBg = 0x10; + + break; + case '008000': + $colorIdxBg = 0x11; + + break; + case '000080': + $colorIdxBg = 0x12; + + break; + case '808000': + $colorIdxBg = 0x13; + + break; + case '800080': + $colorIdxBg = 0x14; + + break; + case '008080': + $colorIdxBg = 0x15; + + break; + case 'C0C0C0': + $colorIdxBg = 0x16; + + break; + case '808080': + $colorIdxBg = 0x17; + + break; + case '9999FF': + $colorIdxBg = 0x18; + + break; + case '993366': + $colorIdxBg = 0x19; + + break; + case 'FFFFCC': + $colorIdxBg = 0x1A; + + break; + case 'CCFFFF': + $colorIdxBg = 0x1B; + + break; + case '660066': + $colorIdxBg = 0x1C; + + break; + case 'FF8080': + $colorIdxBg = 0x1D; + + break; + case '0066CC': + $colorIdxBg = 0x1E; + + break; + case 'CCCCFF': + $colorIdxBg = 0x1F; + + break; + case '000080': + $colorIdxBg = 0x20; + + break; + case 'FF00FF': + $colorIdxBg = 0x21; + + break; + case 'FFFF00': + $colorIdxBg = 0x22; + + break; + case '00FFFF': + $colorIdxBg = 0x23; + + break; + case '800080': + $colorIdxBg = 0x24; + + break; + case '800000': + $colorIdxBg = 0x25; + + break; + case '008080': + $colorIdxBg = 0x26; + + break; + case '0000FF': + $colorIdxBg = 0x27; + + break; + case '00CCFF': + $colorIdxBg = 0x28; + + break; + case 'CCFFFF': + $colorIdxBg = 0x29; + + break; + case 'CCFFCC': + $colorIdxBg = 0x2A; + + break; + case 'FFFF99': + $colorIdxBg = 0x2B; + + break; + case '99CCFF': + $colorIdxBg = 0x2C; + + break; + case 'FF99CC': + $colorIdxBg = 0x2D; + + break; + case 'CC99FF': + $colorIdxBg = 0x2E; + + break; + case 'FFCC99': + $colorIdxBg = 0x2F; + + break; + case '3366FF': + $colorIdxBg = 0x30; + + break; + case '33CCCC': + $colorIdxBg = 0x31; + + break; + case '99CC00': + $colorIdxBg = 0x32; + + break; + case 'FFCC00': + $colorIdxBg = 0x33; + + break; + case 'FF9900': + $colorIdxBg = 0x34; + + break; + case 'FF6600': + $colorIdxBg = 0x35; + + break; + case '666699': + $colorIdxBg = 0x36; + + break; + case '969696': + $colorIdxBg = 0x37; + + break; + case '003366': + $colorIdxBg = 0x38; + + break; + case '339966': + $colorIdxBg = 0x39; + + break; + case '003300': + $colorIdxBg = 0x3A; + + break; + case '333300': + $colorIdxBg = 0x3B; + + break; + case '993300': + $colorIdxBg = 0x3C; + + break; + case '993366': + $colorIdxBg = 0x3D; + + break; + case '333399': + $colorIdxBg = 0x3E; + + break; + case '333333': + $colorIdxBg = 0x3F; + + break; + default: + $colorIdxBg = 0x41; + + break; + } + // Fg Color + switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) { + case '000000': + $colorIdxFg = 0x08; + + break; + case 'FFFFFF': + $colorIdxFg = 0x09; + + break; + case 'FF0000': + $colorIdxFg = 0x0A; + + break; + case '00FF00': + $colorIdxFg = 0x0B; + + break; + case '0000FF': + $colorIdxFg = 0x0C; + + break; + case 'FFFF00': + $colorIdxFg = 0x0D; + + break; + case 'FF00FF': + $colorIdxFg = 0x0E; + + break; + case '00FFFF': + $colorIdxFg = 0x0F; + + break; + case '800000': + $colorIdxFg = 0x10; + + break; + case '008000': + $colorIdxFg = 0x11; + + break; + case '000080': + $colorIdxFg = 0x12; + + break; + case '808000': + $colorIdxFg = 0x13; + + break; + case '800080': + $colorIdxFg = 0x14; + + break; + case '008080': + $colorIdxFg = 0x15; + + break; + case 'C0C0C0': + $colorIdxFg = 0x16; + + break; + case '808080': + $colorIdxFg = 0x17; + + break; + case '9999FF': + $colorIdxFg = 0x18; + + break; + case '993366': + $colorIdxFg = 0x19; + + break; + case 'FFFFCC': + $colorIdxFg = 0x1A; + + break; + case 'CCFFFF': + $colorIdxFg = 0x1B; + + break; + case '660066': + $colorIdxFg = 0x1C; + + break; + case 'FF8080': + $colorIdxFg = 0x1D; + + break; + case '0066CC': + $colorIdxFg = 0x1E; + + break; + case 'CCCCFF': + $colorIdxFg = 0x1F; + + break; + case '000080': + $colorIdxFg = 0x20; + + break; + case 'FF00FF': + $colorIdxFg = 0x21; + + break; + case 'FFFF00': + $colorIdxFg = 0x22; + + break; + case '00FFFF': + $colorIdxFg = 0x23; + + break; + case '800080': + $colorIdxFg = 0x24; + + break; + case '800000': + $colorIdxFg = 0x25; + + break; + case '008080': + $colorIdxFg = 0x26; + + break; + case '0000FF': + $colorIdxFg = 0x27; + + break; + case '00CCFF': + $colorIdxFg = 0x28; + + break; + case 'CCFFFF': + $colorIdxFg = 0x29; + + break; + case 'CCFFCC': + $colorIdxFg = 0x2A; + + break; + case 'FFFF99': + $colorIdxFg = 0x2B; + + break; + case '99CCFF': + $colorIdxFg = 0x2C; + + break; + case 'FF99CC': + $colorIdxFg = 0x2D; + + break; + case 'CC99FF': + $colorIdxFg = 0x2E; + + break; + case 'FFCC99': + $colorIdxFg = 0x2F; + + break; + case '3366FF': + $colorIdxFg = 0x30; + + break; + case '33CCCC': + $colorIdxFg = 0x31; + + break; + case '99CC00': + $colorIdxFg = 0x32; + + break; + case 'FFCC00': + $colorIdxFg = 0x33; + + break; + case 'FF9900': + $colorIdxFg = 0x34; + + break; + case 'FF6600': + $colorIdxFg = 0x35; + + break; + case '666699': + $colorIdxFg = 0x36; + + break; + case '969696': + $colorIdxFg = 0x37; + + break; + case '003366': + $colorIdxFg = 0x38; + + break; + case '339966': + $colorIdxFg = 0x39; + + break; + case '003300': + $colorIdxFg = 0x3A; + + break; + case '333300': + $colorIdxFg = 0x3B; + + break; + case '993300': + $colorIdxFg = 0x3C; + + break; + case '993366': + $colorIdxFg = 0x3D; + + break; + case '333399': + $colorIdxFg = 0x3E; + + break; + case '333333': + $colorIdxFg = 0x3F; + + break; + default: + $colorIdxFg = 0x40; + + break; + } + $dataBlockFill = pack('v', $blockFillPatternStyle); + $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7)); + } + if ($bFormatProt == 1) { + $dataBlockProtection = 0; + if ($conditional->getStyle()->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED) { + $dataBlockProtection = 1; + } + if ($conditional->getStyle()->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED) { + $dataBlockProtection = 1 << 1; + } + } + + $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000); + if ($bFormatFont == 1) { // Block Formatting : OK + $data .= $dataBlockFont; + } + if ($bFormatAlign == 1) { + $data .= $dataBlockAlign; + } + if ($bFormatBorder == 1) { + $data .= $dataBlockBorder; + } + if ($bFormatFill == 1) { // Block Formatting : OK + $data .= $dataBlockFill; + } + if ($bFormatProt == 1) { + $data .= $dataBlockProtection; + } + if ($operand1 !== null) { + $data .= $operand1; + } + if ($operand2 !== null) { + $data .= $operand2; + } + $header = pack('vv', $record, strlen($data)); + $this->append($header . $data); + } + + /** + * Write CFHeader record. + */ + private function writeCFHeader(): void + { + $record = 0x01B0; // Record identifier + $length = 0x0016; // Bytes to follow + + $numColumnMin = null; + $numColumnMax = null; + $numRowMin = null; + $numRowMax = null; + $arrConditional = []; + foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) { + foreach ($conditionalStyles as $conditional) { + if ( + $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION + || $conditional->getConditionType() == Conditional::CONDITION_CELLIS + ) { + if (!in_array($conditional->getHashCode(), $arrConditional)) { + $arrConditional[] = $conditional->getHashCode(); + } + // Cells + $arrCoord = Coordinate::coordinateFromString($cellCoordinate); + if (!is_numeric($arrCoord[0])) { + $arrCoord[0] = Coordinate::columnIndexFromString($arrCoord[0]); + } + if ($numColumnMin === null || ($numColumnMin > $arrCoord[0])) { + $numColumnMin = $arrCoord[0]; + } + if ($numColumnMax === null || ($numColumnMax < $arrCoord[0])) { + $numColumnMax = $arrCoord[0]; + } + if ($numRowMin === null || ($numRowMin > $arrCoord[1])) { + $numRowMin = $arrCoord[1]; + } + if ($numRowMax === null || ($numRowMax < $arrCoord[1])) { + $numRowMax = $arrCoord[1]; + } + } + } + } + $needRedraw = 1; + $cellRange = pack('vvvv', $numRowMin - 1, $numRowMax - 1, $numColumnMin - 1, $numColumnMax - 1); + + $header = pack('vv', $record, $length); + $data = pack('vv', count($arrConditional), $needRedraw); + $data .= $cellRange; + $data .= pack('v', 0x0001); + $data .= $cellRange; + $this->append($header . $data); + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php new file mode 100644 index 00000000000..3e8169b3068 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php @@ -0,0 +1,548 @@ + +// * +// * The majority of this is _NOT_ my code. I simply ported it from the +// * PERL Spreadsheet::WriteExcel module. +// * +// * The author of the Spreadsheet::WriteExcel module is John McNamara +// * +// * +// * I _DO_ maintain this code, and John McNamara has nothing to do with the +// * porting of this code to PHP. Any questions directly related to this +// * class library should be directed to me. +// * +// * License Information: +// * +// * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +// * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +// * +// * This library is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 2.1 of the License, or (at your option) any later version. +// * +// * This library is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public +// * License along with this library; if not, write to the Free Software +// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// */ +class Xf +{ + /** + * Style XF or a cell XF ? + * + * @var bool + */ + private $isStyleXf; + + /** + * Index to the FONT record. Index 4 does not exist. + * + * @var int + */ + private $fontIndex; + + /** + * An index (2 bytes) to a FORMAT record (number format). + * + * @var int + */ + private $numberFormatIndex; + + /** + * 1 bit, apparently not used. + * + * @var int + */ + private $textJustLast; + + /** + * The cell's foreground color. + * + * @var int + */ + private $foregroundColor; + + /** + * The cell's background color. + * + * @var int + */ + private $backgroundColor; + + /** + * Color of the bottom border of the cell. + * + * @var int + */ + private $bottomBorderColor; + + /** + * Color of the top border of the cell. + * + * @var int + */ + private $topBorderColor; + + /** + * Color of the left border of the cell. + * + * @var int + */ + private $leftBorderColor; + + /** + * Color of the right border of the cell. + * + * @var int + */ + private $rightBorderColor; + + /** + * Constructor. + * + * @param Style $style The XF format + */ + public function __construct(Style $style) + { + $this->isStyleXf = false; + $this->fontIndex = 0; + + $this->numberFormatIndex = 0; + + $this->textJustLast = 0; + + $this->foregroundColor = 0x40; + $this->backgroundColor = 0x41; + + $this->_diag = 0; + + $this->bottomBorderColor = 0x40; + $this->topBorderColor = 0x40; + $this->leftBorderColor = 0x40; + $this->rightBorderColor = 0x40; + $this->_diag_color = 0x40; + $this->_style = $style; + } + + /** + * Generate an Excel BIFF XF record (style or cell). + * + * @return string The XF record + */ + public function writeXf() + { + // Set the type of the XF record and some of the attributes. + if ($this->isStyleXf) { + $style = 0xFFF5; + } else { + $style = self::mapLocked($this->_style->getProtection()->getLocked()); + $style |= self::mapHidden($this->_style->getProtection()->getHidden()) << 1; + } + + // Flags to indicate if attributes have been set. + $atr_num = ($this->numberFormatIndex != 0) ? 1 : 0; + $atr_fnt = ($this->fontIndex != 0) ? 1 : 0; + $atr_alc = ((int) $this->_style->getAlignment()->getWrapText()) ? 1 : 0; + $atr_bdr = (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) || + self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) || + self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) || + self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle())) ? 1 : 0; + $atr_pat = (($this->foregroundColor != 0x40) || + ($this->backgroundColor != 0x41) || + self::mapFillType($this->_style->getFill()->getFillType())) ? 1 : 0; + $atr_prot = self::mapLocked($this->_style->getProtection()->getLocked()) + | self::mapHidden($this->_style->getProtection()->getHidden()); + + // Zero the default border colour if the border has not been set. + if (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) == 0) { + $this->bottomBorderColor = 0; + } + if (self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) == 0) { + $this->topBorderColor = 0; + } + if (self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) == 0) { + $this->rightBorderColor = 0; + } + if (self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) == 0) { + $this->leftBorderColor = 0; + } + if (self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) == 0) { + $this->_diag_color = 0; + } + + $record = 0x00E0; // Record identifier + $length = 0x0014; // Number of bytes to follow + + $ifnt = $this->fontIndex; // Index to FONT record + $ifmt = $this->numberFormatIndex; // Index to FORMAT record + + $align = $this->mapHAlign($this->_style->getAlignment()->getHorizontal()); // Alignment + $align |= (int) $this->_style->getAlignment()->getWrapText() << 3; + $align |= self::mapVAlign($this->_style->getAlignment()->getVertical()) << 4; + $align |= $this->textJustLast << 7; + + $used_attrib = $atr_num << 2; + $used_attrib |= $atr_fnt << 3; + $used_attrib |= $atr_alc << 4; + $used_attrib |= $atr_bdr << 5; + $used_attrib |= $atr_pat << 6; + $used_attrib |= $atr_prot << 7; + + $icv = $this->foregroundColor; // fg and bg pattern colors + $icv |= $this->backgroundColor << 7; + + $border1 = self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()); // Border line style and color + $border1 |= self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) << 4; + $border1 |= self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) << 8; + $border1 |= self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) << 12; + $border1 |= $this->leftBorderColor << 16; + $border1 |= $this->rightBorderColor << 23; + + $diagonalDirection = $this->_style->getBorders()->getDiagonalDirection(); + $diag_tl_to_rb = $diagonalDirection == Borders::DIAGONAL_BOTH + || $diagonalDirection == Borders::DIAGONAL_DOWN; + $diag_tr_to_lb = $diagonalDirection == Borders::DIAGONAL_BOTH + || $diagonalDirection == Borders::DIAGONAL_UP; + $border1 |= $diag_tl_to_rb << 30; + $border1 |= $diag_tr_to_lb << 31; + + $border2 = $this->topBorderColor; // Border color + $border2 |= $this->bottomBorderColor << 7; + $border2 |= $this->_diag_color << 14; + $border2 |= self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) << 21; + $border2 |= self::mapFillType($this->_style->getFill()->getFillType()) << 26; + + $header = pack('vv', $record, $length); + + //BIFF8 options: identation, shrinkToFit and text direction + $biff8_options = $this->_style->getAlignment()->getIndent(); + $biff8_options |= (int) $this->_style->getAlignment()->getShrinkToFit() << 4; + + $data = pack('vvvC', $ifnt, $ifmt, $style, $align); + $data .= pack('CCC', self::mapTextRotation($this->_style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib); + $data .= pack('VVv', $border1, $border2, $icv); + + return $header . $data; + } + + /** + * Is this a style XF ? + * + * @param bool $value + */ + public function setIsStyleXf($value): void + { + $this->isStyleXf = $value; + } + + /** + * Sets the cell's bottom border color. + * + * @param int $colorIndex Color index + */ + public function setBottomColor($colorIndex): void + { + $this->bottomBorderColor = $colorIndex; + } + + /** + * Sets the cell's top border color. + * + * @param int $colorIndex Color index + */ + public function setTopColor($colorIndex): void + { + $this->topBorderColor = $colorIndex; + } + + /** + * Sets the cell's left border color. + * + * @param int $colorIndex Color index + */ + public function setLeftColor($colorIndex): void + { + $this->leftBorderColor = $colorIndex; + } + + /** + * Sets the cell's right border color. + * + * @param int $colorIndex Color index + */ + public function setRightColor($colorIndex): void + { + $this->rightBorderColor = $colorIndex; + } + + /** + * Sets the cell's diagonal border color. + * + * @param int $colorIndex Color index + */ + public function setDiagColor($colorIndex): void + { + $this->_diag_color = $colorIndex; + } + + /** + * Sets the cell's foreground color. + * + * @param int $colorIndex Color index + */ + public function setFgColor($colorIndex): void + { + $this->foregroundColor = $colorIndex; + } + + /** + * Sets the cell's background color. + * + * @param int $colorIndex Color index + */ + public function setBgColor($colorIndex): void + { + $this->backgroundColor = $colorIndex; + } + + /** + * Sets the index to the number format record + * It can be date, time, currency, etc... + * + * @param int $numberFormatIndex Index to format record + */ + public function setNumberFormatIndex($numberFormatIndex): void + { + $this->numberFormatIndex = $numberFormatIndex; + } + + /** + * Set the font index. + * + * @param int $value Font index, note that value 4 does not exist + */ + public function setFontIndex($value): void + { + $this->fontIndex = $value; + } + + /** + * Map of BIFF2-BIFF8 codes for border styles. + * + * @var array of int + */ + private static $mapBorderStyles = [ + Border::BORDER_NONE => 0x00, + Border::BORDER_THIN => 0x01, + Border::BORDER_MEDIUM => 0x02, + Border::BORDER_DASHED => 0x03, + Border::BORDER_DOTTED => 0x04, + Border::BORDER_THICK => 0x05, + Border::BORDER_DOUBLE => 0x06, + Border::BORDER_HAIR => 0x07, + Border::BORDER_MEDIUMDASHED => 0x08, + Border::BORDER_DASHDOT => 0x09, + Border::BORDER_MEDIUMDASHDOT => 0x0A, + Border::BORDER_DASHDOTDOT => 0x0B, + Border::BORDER_MEDIUMDASHDOTDOT => 0x0C, + Border::BORDER_SLANTDASHDOT => 0x0D, + ]; + + /** + * Map border style. + * + * @param string $borderStyle + * + * @return int + */ + private static function mapBorderStyle($borderStyle) + { + if (isset(self::$mapBorderStyles[$borderStyle])) { + return self::$mapBorderStyles[$borderStyle]; + } + + return 0x00; + } + + /** + * Map of BIFF2-BIFF8 codes for fill types. + * + * @var array of int + */ + private static $mapFillTypes = [ + Fill::FILL_NONE => 0x00, + Fill::FILL_SOLID => 0x01, + Fill::FILL_PATTERN_MEDIUMGRAY => 0x02, + Fill::FILL_PATTERN_DARKGRAY => 0x03, + Fill::FILL_PATTERN_LIGHTGRAY => 0x04, + Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05, + Fill::FILL_PATTERN_DARKVERTICAL => 0x06, + Fill::FILL_PATTERN_DARKDOWN => 0x07, + Fill::FILL_PATTERN_DARKUP => 0x08, + Fill::FILL_PATTERN_DARKGRID => 0x09, + Fill::FILL_PATTERN_DARKTRELLIS => 0x0A, + Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B, + Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C, + Fill::FILL_PATTERN_LIGHTDOWN => 0x0D, + Fill::FILL_PATTERN_LIGHTUP => 0x0E, + Fill::FILL_PATTERN_LIGHTGRID => 0x0F, + Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10, + Fill::FILL_PATTERN_GRAY125 => 0x11, + Fill::FILL_PATTERN_GRAY0625 => 0x12, + Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8 + Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8 + ]; + + /** + * Map fill type. + * + * @param string $fillType + * + * @return int + */ + private static function mapFillType($fillType) + { + if (isset(self::$mapFillTypes[$fillType])) { + return self::$mapFillTypes[$fillType]; + } + + return 0x00; + } + + /** + * Map of BIFF2-BIFF8 codes for horizontal alignment. + * + * @var array of int + */ + private static $mapHAlignments = [ + Alignment::HORIZONTAL_GENERAL => 0, + Alignment::HORIZONTAL_LEFT => 1, + Alignment::HORIZONTAL_CENTER => 2, + Alignment::HORIZONTAL_RIGHT => 3, + Alignment::HORIZONTAL_FILL => 4, + Alignment::HORIZONTAL_JUSTIFY => 5, + Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6, + ]; + + /** + * Map to BIFF2-BIFF8 codes for horizontal alignment. + * + * @param string $hAlign + * + * @return int + */ + private function mapHAlign($hAlign) + { + if (isset(self::$mapHAlignments[$hAlign])) { + return self::$mapHAlignments[$hAlign]; + } + + return 0; + } + + /** + * Map of BIFF2-BIFF8 codes for vertical alignment. + * + * @var array of int + */ + private static $mapVAlignments = [ + Alignment::VERTICAL_TOP => 0, + Alignment::VERTICAL_CENTER => 1, + Alignment::VERTICAL_BOTTOM => 2, + Alignment::VERTICAL_JUSTIFY => 3, + ]; + + /** + * Map to BIFF2-BIFF8 codes for vertical alignment. + * + * @param string $vAlign + * + * @return int + */ + private static function mapVAlign($vAlign) + { + if (isset(self::$mapVAlignments[$vAlign])) { + return self::$mapVAlignments[$vAlign]; + } + + return 2; + } + + /** + * Map to BIFF8 codes for text rotation angle. + * + * @param int $textRotation + * + * @return int + */ + private static function mapTextRotation($textRotation) + { + if ($textRotation >= 0) { + return $textRotation; + } elseif ($textRotation == -165) { + return 255; + } elseif ($textRotation < 0) { + return 90 - $textRotation; + } + } + + /** + * Map locked. + * + * @param string $locked + * + * @return int + */ + private static function mapLocked($locked) + { + switch ($locked) { + case Protection::PROTECTION_INHERIT: + return 1; + case Protection::PROTECTION_PROTECTED: + return 1; + case Protection::PROTECTION_UNPROTECTED: + return 0; + default: + return 1; + } + } + + /** + * Map hidden. + * + * @param string $hidden + * + * @return int + */ + private static function mapHidden($hidden) + { + switch ($hidden) { + case Protection::PROTECTION_INHERIT: + return 0; + case Protection::PROTECTION_PROTECTED: + return 1; + case Protection::PROTECTION_UNPROTECTED: + return 0; + default: + return 0; + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php index 58897639024..d71541c8053 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php @@ -24,6 +24,9 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme; use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook; use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet; use ZipArchive; +use ZipStream\Exception\OverflowException; +use ZipStream\Option\Archive; +use ZipStream\ZipStream; class Xlsx extends BaseWriter { @@ -105,9 +108,14 @@ class Xlsx extends BaseWriter private $drawingHashTable; /** - * Create a new Xlsx Writer. + * Private handle for zip stream. * - * @param Spreadsheet $spreadsheet + * @var ZipStream + */ + private $zip; + + /** + * Create a new Xlsx Writer. */ public function __construct(Spreadsheet $spreadsheet) { @@ -166,276 +174,248 @@ class Xlsx extends BaseWriter /** * Save PhpSpreadsheet to file. * - * @param string $pFilename - * - * @throws WriterException + * @param resource|string $pFilename */ - public function save($pFilename) + public function save($pFilename): void { - if ($this->spreadSheet !== null) { - // garbage collect - $this->spreadSheet->garbageCollect(); + // garbage collect + $this->pathNames = []; + $this->spreadSheet->garbageCollect(); - // If $pFilename is php://output or php://stdout, make it a temporary file... - $originalFilename = $pFilename; - if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') { - $pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp'); - if ($pFilename == '') { - $pFilename = $originalFilename; - } - } + $this->openFileHandle($pFilename); - $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog(); - Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false); - $saveDateReturnType = Functions::getReturnDateType(); - Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); + $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog(); + Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false); + $saveDateReturnType = Functions::getReturnDateType(); + Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - // Create string lookup table - $this->stringTable = []; - for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { - $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable); - } - - // Create styles dictionaries - $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet)); - $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet)); - $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet)); - $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet)); - $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet)); - $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet)); - - // Create drawing dictionary - $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet)); - - $zip = new ZipArchive(); - - if (file_exists($pFilename)) { - unlink($pFilename); - } - // Try opening the ZIP file - if ($zip->open($pFilename, ZipArchive::OVERWRITE) !== true) { - if ($zip->open($pFilename, ZipArchive::CREATE) !== true) { - throw new WriterException('Could not open ' . $pFilename . ' for writing.'); - } - } - - // Add [Content_Types].xml to ZIP file - $zip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts)); - - //if hasMacros, add the vbaProject.bin file, Certificate file(if exists) - if ($this->spreadSheet->hasMacros()) { - $macrosCode = $this->spreadSheet->getMacrosCode(); - if ($macrosCode !== null) { - // we have the code ? - $zip->addFromString('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin - if ($this->spreadSheet->hasMacrosCertificate()) { - //signed macros ? - // Yes : add the certificate file and the related rels file - $zip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate()); - $zip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet)); - } - } - } - //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels) - if ($this->spreadSheet->hasRibbon()) { - $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target'); - $zip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data')); - if ($this->spreadSheet->hasRibbonBinObjects()) { - $tmpRootPath = dirname($tmpRibbonTarget) . '/'; - $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write - foreach ($ribbonBinObjects as $aPath => $aContent) { - $zip->addFromString($tmpRootPath . $aPath, $aContent); - } - //the rels for files - $zip->addFromString($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet)); - } - } - - // Add relationships to ZIP file - $zip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet)); - $zip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet)); - - // Add document properties to ZIP file - $zip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet)); - $zip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet)); - $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet); - if ($customPropertiesPart !== null) { - $zip->addFromString('docProps/custom.xml', $customPropertiesPart); - } - - // Add theme to ZIP file - $zip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet)); - - // Add string table to ZIP file - $zip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable)); - - // Add styles to ZIP file - $zip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet)); - - // Add workbook to ZIP file - $zip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas)); - - $chartCount = 0; - // Add worksheets - for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { - $zip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts)); - if ($this->includeCharts) { - $charts = $this->spreadSheet->getSheet($i)->getChartCollection(); - if (count($charts) > 0) { - foreach ($charts as $chart) { - $zip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas)); - ++$chartCount; - } - } - } - } - - $chartRef1 = 0; - // Add worksheet relationships (drawings, ...) - for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { - // Add relationships - $zip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts)); - - // Add unparsedLoadedData - $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName(); - $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData(); - if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) { - foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) { - $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); - } - } - if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) { - foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) { - $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); - } - } - - $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection(); - $drawingCount = count($drawings); - if ($this->includeCharts) { - $chartCount = $this->spreadSheet->getSheet($i)->getChartCount(); - } - - // Add drawing and image relationship parts - if (($drawingCount > 0) || ($chartCount > 0)) { - // Drawing relationships - $zip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts)); - - // Drawings - $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); - } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) { - // Drawings - $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); - } - - // Add unparsed drawings - if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) { - foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) { - $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']); - if ($drawingFile !== false) { - $drawingFile = ltrim($drawingFile, '.'); - $zip->addFromString('xl' . $drawingFile, $drawingXml); - } - } - } - - // Add comment relationship parts - if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { - // VML Comments - $zip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i))); - - // Comments - $zip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i))); - } - - // Add unparsed relationship parts - if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) { - foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) { - $zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']); - } - } - - // Add header/footer relationship parts - if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) { - // VML Drawings - $zip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i))); - - // VML Drawing relationships - $zip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i))); - - // Media - foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) { - $zip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath())); - } - } - } - - // Add media - for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) { - if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) { - $imageContents = null; - $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath(); - if (strpos($imagePath, 'zip://') !== false) { - $imagePath = substr($imagePath, 6); - $imagePathSplitted = explode('#', $imagePath); - - $imageZip = new ZipArchive(); - $imageZip->open($imagePathSplitted[0]); - $imageContents = $imageZip->getFromName($imagePathSplitted[1]); - $imageZip->close(); - unset($imageZip); - } else { - $imageContents = file_get_contents($imagePath); - } - - $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); - } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) { - ob_start(); - call_user_func( - $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(), - $this->getDrawingHashTable()->getByIndex($i)->getImageResource() - ); - $imageContents = ob_get_contents(); - ob_end_clean(); - - $zip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); - } - } - - Functions::setReturnDateType($saveDateReturnType); - Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); - - // Close file - if ($zip->close() === false) { - throw new WriterException("Could not close zip file $pFilename."); - } - - // If a temporary file was used, copy it to the correct file stream - if ($originalFilename != $pFilename) { - if (copy($pFilename, $originalFilename) === false) { - throw new WriterException("Could not copy temporary zip file $pFilename to $originalFilename."); - } - @unlink($pFilename); - } - } else { - throw new WriterException('PhpSpreadsheet object unassigned.'); + // Create string lookup table + $this->stringTable = []; + for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { + $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable); } + + // Create styles dictionaries + $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet)); + $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet)); + $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet)); + $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet)); + $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet)); + $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet)); + + // Create drawing dictionary + $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet)); + + $options = new Archive(); + $options->setEnableZip64(false); + $options->setOutputStream($this->fileHandle); + + $this->zip = new ZipStream(null, $options); + + // Add [Content_Types].xml to ZIP file + $this->addZipFile('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts)); + + //if hasMacros, add the vbaProject.bin file, Certificate file(if exists) + if ($this->spreadSheet->hasMacros()) { + $macrosCode = $this->spreadSheet->getMacrosCode(); + if ($macrosCode !== null) { + // we have the code ? + $this->addZipFile('xl/vbaProject.bin', $macrosCode); //allways in 'xl', allways named vbaProject.bin + if ($this->spreadSheet->hasMacrosCertificate()) { + //signed macros ? + // Yes : add the certificate file and the related rels file + $this->addZipFile('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate()); + $this->addZipFile('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet)); + } + } + } + //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels) + if ($this->spreadSheet->hasRibbon()) { + $tmpRibbonTarget = $this->spreadSheet->getRibbonXMLData('target'); + $this->addZipFile($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data')); + if ($this->spreadSheet->hasRibbonBinObjects()) { + $tmpRootPath = dirname($tmpRibbonTarget) . '/'; + $ribbonBinObjects = $this->spreadSheet->getRibbonBinObjects('data'); //the files to write + foreach ($ribbonBinObjects as $aPath => $aContent) { + $this->addZipFile($tmpRootPath . $aPath, $aContent); + } + //the rels for files + $this->addZipFile($tmpRootPath . '_rels/' . basename($tmpRibbonTarget) . '.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet)); + } + } + + // Add relationships to ZIP file + $this->addZipFile('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet)); + $this->addZipFile('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet)); + + // Add document properties to ZIP file + $this->addZipFile('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet)); + $this->addZipFile('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet)); + $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet); + if ($customPropertiesPart !== null) { + $this->addZipFile('docProps/custom.xml', $customPropertiesPart); + } + + // Add theme to ZIP file + $this->addZipFile('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet)); + + // Add string table to ZIP file + $this->addZipFile('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable)); + + // Add styles to ZIP file + $this->addZipFile('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet)); + + // Add workbook to ZIP file + $this->addZipFile('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas)); + + $chartCount = 0; + // Add worksheets + for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { + $this->addZipFile('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts)); + if ($this->includeCharts) { + $charts = $this->spreadSheet->getSheet($i)->getChartCollection(); + if (count($charts) > 0) { + foreach ($charts as $chart) { + $this->addZipFile('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas)); + ++$chartCount; + } + } + } + } + + $chartRef1 = 0; + // Add worksheet relationships (drawings, ...) + for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { + // Add relationships + $this->addZipFile('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts)); + + // Add unparsedLoadedData + $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName(); + $unparsedLoadedData = $this->spreadSheet->getUnparsedLoadedData(); + if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) { + foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) { + $this->addZipFile($ctrlProp['filePath'], $ctrlProp['content']); + } + } + if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) { + foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) { + $this->addZipFile($ctrlProp['filePath'], $ctrlProp['content']); + } + } + + $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection(); + $drawingCount = count($drawings); + if ($this->includeCharts) { + $chartCount = $this->spreadSheet->getSheet($i)->getChartCount(); + } + + // Add drawing and image relationship parts + if (($drawingCount > 0) || ($chartCount > 0)) { + // Drawing relationships + $this->addZipFile('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts)); + + // Drawings + $this->addZipFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); + } elseif (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) { + // Drawings + $this->addZipFile('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); + } + + // Add unparsed drawings + if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) { + foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) { + $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']); + if ($drawingFile !== false) { + $drawingFile = ltrim($drawingFile, '.'); + $this->addZipFile('xl' . $drawingFile, $drawingXml); + } + } + } + + // Add comment relationship parts + if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { + // VML Comments + $this->addZipFile('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i))); + + // Comments + $this->addZipFile('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i))); + } + + // Add unparsed relationship parts + if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) { + foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) { + $this->addZipFile($vmlDrawing['filePath'], $vmlDrawing['content']); + } + } + + // Add header/footer relationship parts + if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) { + // VML Drawings + $this->addZipFile('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i))); + + // VML Drawing relationships + $this->addZipFile('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i))); + + // Media + foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) { + $this->addZipFile('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath())); + } + } + } + + // Add media + for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) { + if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) { + $imageContents = null; + $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath(); + if (strpos($imagePath, 'zip://') !== false) { + $imagePath = substr($imagePath, 6); + $imagePathSplitted = explode('#', $imagePath); + + $imageZip = new ZipArchive(); + $imageZip->open($imagePathSplitted[0]); + $imageContents = $imageZip->getFromName($imagePathSplitted[1]); + $imageZip->close(); + unset($imageZip); + } else { + $imageContents = file_get_contents($imagePath); + } + + $this->addZipFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); + } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) { + ob_start(); + call_user_func( + $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(), + $this->getDrawingHashTable()->getByIndex($i)->getImageResource() + ); + $imageContents = ob_get_contents(); + ob_end_clean(); + + $this->addZipFile('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents); + } + } + + Functions::setReturnDateType($saveDateReturnType); + Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); + + // Close file + try { + $this->zip->finish(); + } catch (OverflowException $e) { + throw new WriterException('Could not close resource.'); + } + + $this->maybeCloseFileHandle(); } /** * Get Spreadsheet object. * - * @throws WriterException - * * @return Spreadsheet */ public function getSpreadsheet() { - if ($this->spreadSheet !== null) { - return $this->spreadSheet; - } - - throw new WriterException('No Spreadsheet object assigned.'); + return $this->spreadSheet; } /** @@ -443,7 +423,7 @@ class Xlsx extends BaseWriter * * @param Spreadsheet $spreadsheet PhpSpreadsheet object * - * @return Xlsx + * @return $this */ public function setSpreadsheet(Spreadsheet $spreadsheet) { @@ -547,7 +527,7 @@ class Xlsx extends BaseWriter * * @param bool $pValue Office2003 compatibility? * - * @return Xlsx + * @return $this */ public function setOffice2003Compatibility($pValue) { @@ -555,4 +535,14 @@ class Xlsx extends BaseWriter return $this; } + + private $pathNames = []; + + private function addZipFile(string $path, string $content): void + { + if (!in_array($path, $this->pathNames)) { + $this->pathNames[] = $path; + $this->zip->addFile($path, $content); + } + } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index c9c3e055b28..583b262c355 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -26,11 +26,8 @@ class Chart extends WriterPart /** * Write charts to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart * @param mixed $calculateCellValues * - * @throws WriterException - * * @return string XML Output */ public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $calculateCellValues = true) @@ -109,10 +106,8 @@ class Chart extends WriterPart * * @param XMLWriter $objWriter XML Writer * @param Title $title - * - * @throws WriterException */ - private function writeTitle(XMLWriter $objWriter, Title $title = null) + private function writeTitle(XMLWriter $objWriter, ?Title $title = null): void { if ($title === null) { return; @@ -154,10 +149,8 @@ class Chart extends WriterPart * * @param XMLWriter $objWriter XML Writer * @param Legend $legend - * - * @throws WriterException */ - private function writeLegend(XMLWriter $objWriter, Legend $legend = null) + private function writeLegend(XMLWriter $objWriter, ?Legend $legend = null): void { if ($legend === null) { return; @@ -204,18 +197,12 @@ class Chart extends WriterPart * Write Chart Plot Area. * * @param XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pSheet - * @param PlotArea $plotArea * @param Title $xAxisLabel * @param Title $yAxisLabel * @param Axis $xAxis * @param Axis $yAxis - * @param null|GridLines $majorGridlines - * @param null|GridLines $minorGridlines - * - * @throws WriterException */ - private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pSheet, PlotArea $plotArea, Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null) + private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pSheet, PlotArea $plotArea, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null): void { if ($plotArea === null) { return; @@ -344,7 +331,7 @@ class Chart extends WriterPart * @param XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpSpreadsheet\Chart\Layout $chartLayout Chart layout */ - private function writeDataLabels(XMLWriter $objWriter, Layout $chartLayout = null) + private function writeDataLabels(XMLWriter $objWriter, ?Layout $chartLayout = null): void { $objWriter->startElement('c:dLbls'); @@ -394,11 +381,8 @@ class Chart extends WriterPart * @param string $id1 * @param string $id2 * @param bool $isMultiLevelSeries - * @param Axis $yAxis - * - * @throws WriterException */ - private function writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis) + private function writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void { $objWriter->startElement('c:catAx'); @@ -515,13 +499,8 @@ class Chart extends WriterPart * @param string $id1 * @param string $id2 * @param bool $isMultiLevelSeries - * @param Axis $xAxis - * @param GridLines $majorGridlines - * @param GridLines $minorGridlines - * - * @throws WriterException */ - private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines) + private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines): void { $objWriter->startElement('c:valAx'); @@ -999,8 +978,6 @@ class Chart extends WriterPart * * @param PlotArea $plotArea * - * @throws WriterException - * * @return array|string */ private static function getChartType($plotArea) @@ -1064,10 +1041,8 @@ class Chart extends WriterPart * @param bool &$catIsMultiLevelSeries Is category a multi-series category * @param bool &$valIsMultiLevelSeries Is value set a multi-series set * @param string &$plotGroupingType Type of grouping for multi-series values - * - * @throws WriterException */ - private function writePlotGroup($plotGroup, $groupType, $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType) + private function writePlotGroup($plotGroup, $groupType, $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void { if ($plotGroup === null) { return; @@ -1251,7 +1226,7 @@ class Chart extends WriterPart * @param DataSeriesValues $plotSeriesLabel * @param XMLWriter $objWriter XML Writer */ - private function writePlotSeriesLabel($plotSeriesLabel, $objWriter) + private function writePlotSeriesLabel($plotSeriesLabel, $objWriter): void { if ($plotSeriesLabel === null) { return; @@ -1286,7 +1261,7 @@ class Chart extends WriterPart * @param string $groupType Type of plot for dataseries * @param string $dataType Datatype of series values */ - private function writePlotSeriesValues($plotSeriesValues, XMLWriter $objWriter, $groupType, $dataType = 'str') + private function writePlotSeriesValues($plotSeriesValues, XMLWriter $objWriter, $groupType, $dataType = 'str'): void { if ($plotSeriesValues === null) { return; @@ -1376,7 +1351,7 @@ class Chart extends WriterPart * @param DataSeriesValues $plotSeriesValues * @param XMLWriter $objWriter XML Writer */ - private function writeBubbles($plotSeriesValues, $objWriter) + private function writeBubbles($plotSeriesValues, $objWriter): void { if ($plotSeriesValues === null) { return; @@ -1421,7 +1396,7 @@ class Chart extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Layout $layout */ - private function writeLayout(XMLWriter $objWriter, Layout $layout = null) + private function writeLayout(XMLWriter $objWriter, ?Layout $layout = null): void { $objWriter->startElement('c:layout'); @@ -1488,7 +1463,7 @@ class Chart extends WriterPart * * @param XMLWriter $objWriter XML Writer */ - private function writeAlternateContent($objWriter) + private function writeAlternateContent($objWriter): void { $objWriter->startElement('mc:AlternateContent'); $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); @@ -1516,7 +1491,7 @@ class Chart extends WriterPart * * @param XMLWriter $objWriter XML Writer */ - private function writePrintSettings($objWriter) + private function writePrintSettings($objWriter): void { $objWriter->startElement('c:printSettings'); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php index a95298afcdd..73c4308b8b5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php @@ -11,10 +11,6 @@ class Comments extends WriterPart /** * Write comments to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet) @@ -73,10 +69,8 @@ class Comments extends WriterPart * @param string $pCellReference Cell reference * @param Comment $pComment Comment * @param array $pAuthors Array of authors - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeComment(XMLWriter $objWriter, $pCellReference, Comment $pComment, array $pAuthors) + private function writeComment(XMLWriter $objWriter, $pCellReference, Comment $pComment, array $pAuthors): void { // comment $objWriter->startElement('comment'); @@ -94,10 +88,6 @@ class Comments extends WriterPart /** * Write VML comments to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet) @@ -172,7 +162,7 @@ class Comments extends WriterPart * @param string $pCellReference Cell reference, eg: 'A1' * @param Comment $pComment Comment */ - private function writeVMLComment(XMLWriter $objWriter, $pCellReference, Comment $pComment) + private function writeVMLComment(XMLWriter $objWriter, $pCellReference, Comment $pComment): void { // Metadata [$column, $row] = Coordinate::coordinateFromString($pCellReference); diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php index 6b22d7134f9..2cff1a8f6fb 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php @@ -13,11 +13,8 @@ class ContentTypes extends WriterPart /** * Write content types to XML format. * - * @param Spreadsheet $spreadsheet * @param bool $includeCharts Flag indicating if we should include drawing details for charts * - * @throws WriterException - * * @return string XML Output */ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = false) @@ -188,8 +185,6 @@ class ContentTypes extends WriterPart * * @param string $pFile Filename * - * @throws WriterException - * * @return string Mime Type */ private function getImageMimeType($pFile) @@ -209,10 +204,8 @@ class ContentTypes extends WriterPart * @param XMLWriter $objWriter XML Writer * @param string $pPartname Part name * @param string $pContentType Content type - * - * @throws WriterException */ - private function writeDefaultContentType(XMLWriter $objWriter, $pPartname, $pContentType) + private function writeDefaultContentType(XMLWriter $objWriter, $pPartname, $pContentType): void { if ($pPartname != '' && $pContentType != '') { // Write content type @@ -231,10 +224,8 @@ class ContentTypes extends WriterPart * @param XMLWriter $objWriter XML Writer * @param string $pPartname Part name * @param string $pContentType Content type - * - * @throws WriterException */ - private function writeOverrideContentType(XMLWriter $objWriter, $pPartname, $pContentType) + private function writeOverrideContentType(XMLWriter $objWriter, $pPartname, $pContentType): void { if ($pPartname != '' && $pContentType != '') { // Write content type diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php new file mode 100644 index 00000000000..8c3da827a95 --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php @@ -0,0 +1,223 @@ +objWriter = $objWriter; + $this->spreadsheet = $spreadsheet; + } + + public function write(): void + { + // Write defined names + $this->objWriter->startElement('definedNames'); + + // Named ranges + if (count($this->spreadsheet->getDefinedNames()) > 0) { + // Named ranges + $this->writeNamedRangesAndFormulae(); + } + + // Other defined names + $sheetCount = $this->spreadsheet->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { + // NamedRange for autoFilter + $this->writeNamedRangeForAutofilter($this->spreadsheet->getSheet($i), $i); + + // NamedRange for Print_Titles + $this->writeNamedRangeForPrintTitles($this->spreadsheet->getSheet($i), $i); + + // NamedRange for Print_Area + $this->writeNamedRangeForPrintArea($this->spreadsheet->getSheet($i), $i); + } + + $this->objWriter->endElement(); + } + + /** + * Write defined names. + */ + private function writeNamedRangesAndFormulae(): void + { + // Loop named ranges + $definedNames = $this->spreadsheet->getDefinedNames(); + foreach ($definedNames as $definedName) { + $this->writeDefinedName($definedName); + } + } + + /** + * Write Defined Name for named range. + */ + private function writeDefinedName(DefinedName $pDefinedName): void + { + // definedName for named range + $this->objWriter->startElement('definedName'); + $this->objWriter->writeAttribute('name', $pDefinedName->getName()); + if ($pDefinedName->getLocalOnly() && $pDefinedName->getScope() !== null) { + $this->objWriter->writeAttribute('localSheetId', $pDefinedName->getScope()->getParent()->getIndex($pDefinedName->getScope())); + } + + $definedRange = $pDefinedName->getValue(); + $splitCount = preg_match_all( + '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui', + $definedRange, + $splitRanges, + PREG_OFFSET_CAPTURE + ); + + $lengths = array_map('strlen', array_column($splitRanges[0], 0)); + $offsets = array_column($splitRanges[0], 1); + + $worksheets = $splitRanges[2]; + $columns = $splitRanges[6]; + $rows = $splitRanges[7]; + + while ($splitCount > 0) { + --$splitCount; + $length = $lengths[$splitCount]; + $offset = $offsets[$splitCount]; + $worksheet = $worksheets[$splitCount][0]; + $column = $columns[$splitCount][0]; + $row = $rows[$splitCount][0]; + + $newRange = ''; + if (empty($worksheet)) { + if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) { + // We should have a worksheet + $worksheet = $pDefinedName->getWorksheet()->getTitle(); + } + } else { + $worksheet = str_replace("''", "'", trim($worksheet, "'")); + } + if (!empty($worksheet)) { + $newRange = "'" . str_replace("'", "''", $worksheet) . "'!"; + } + + if (!empty($column)) { + $newRange .= $column; + } + if (!empty($row)) { + $newRange .= $row; + } + + $definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length); + } + + if (substr($definedRange, 0, 1) === '=') { + $definedRange = substr($definedRange, 1); + } + + $this->objWriter->writeRawData($definedRange); + + $this->objWriter->endElement(); + } + + /** + * Write Defined Name for autoFilter. + */ + private function writeNamedRangeForAutofilter(Worksheet $pSheet, int $pSheetId = 0): void + { + // NamedRange for autoFilter + $autoFilterRange = $pSheet->getAutoFilter()->getRange(); + if (!empty($autoFilterRange)) { + $this->objWriter->startElement('definedName'); + $this->objWriter->writeAttribute('name', '_xlnm._FilterDatabase'); + $this->objWriter->writeAttribute('localSheetId', $pSheetId); + $this->objWriter->writeAttribute('hidden', '1'); + + // Create absolute coordinate and write as raw text + $range = Coordinate::splitRange($autoFilterRange); + $range = $range[0]; + // Strip any worksheet ref so we can make the cell ref absolute + [$ws, $range[0]] = Worksheet::extractSheetTitle($range[0], true); + + $range[0] = Coordinate::absoluteCoordinate($range[0]); + $range[1] = Coordinate::absoluteCoordinate($range[1]); + $range = implode(':', $range); + + $this->objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range); + + $this->objWriter->endElement(); + } + } + + /** + * Write Defined Name for PrintTitles. + */ + private function writeNamedRangeForPrintTitles(Worksheet $pSheet, int $pSheetId = 0): void + { + // NamedRange for PrintTitles + if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { + $this->objWriter->startElement('definedName'); + $this->objWriter->writeAttribute('name', '_xlnm.Print_Titles'); + $this->objWriter->writeAttribute('localSheetId', $pSheetId); + + // Setting string + $settingString = ''; + + // Columns to repeat + if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { + $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft(); + + $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; + } + + // Rows to repeat + if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { + if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { + $settingString .= ','; + } + + $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop(); + + $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; + } + + $this->objWriter->writeRawData($settingString); + + $this->objWriter->endElement(); + } + } + + /** + * Write Defined Name for PrintTitles. + */ + private function writeNamedRangeForPrintArea(Worksheet $pSheet, int $pSheetId = 0): void + { + // NamedRange for PrintArea + if ($pSheet->getPageSetup()->isPrintAreaSet()) { + $this->objWriter->startElement('definedName'); + $this->objWriter->writeAttribute('name', '_xlnm.Print_Area'); + $this->objWriter->writeAttribute('localSheetId', $pSheetId); + + // Print area + $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea()); + + $chunks = []; + foreach ($printArea as $printAreaRect) { + $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]); + $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]); + $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect); + } + + $this->objWriter->writeRawData(implode(',', $chunks)); + + $this->objWriter->endElement(); + } + } +} diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php index 2a18d5c7e3d..bcbc2379df4 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php @@ -10,10 +10,6 @@ class DocProps extends WriterPart /** * Write docProps/app.xml to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeDocPropsApp(Spreadsheet $spreadsheet) @@ -109,10 +105,6 @@ class DocProps extends WriterPart /** * Write docProps/core.xml to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeDocPropsCore(Spreadsheet $spreadsheet) @@ -178,10 +170,6 @@ class DocProps extends WriterPart /** * Write docProps/custom.xml to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeDocPropsCustom(Spreadsheet $spreadsheet) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php index 08256a1d5b7..1713b982297 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php @@ -14,11 +14,8 @@ class Drawing extends WriterPart /** * Write drawings to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet * @param bool $includeCharts Flag indicating if we should include drawing details for charts * - * @throws WriterException - * * @return string XML Output */ public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $includeCharts = false) @@ -82,10 +79,9 @@ class Drawing extends WriterPart * Write drawings to XML format. * * @param XMLWriter $objWriter XML Writer - * @param \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart * @param int $pRelationId */ - public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $pRelationId = -1) + public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $pChart, $pRelationId = -1): void { $tl = $pChart->getTopLeftPosition(); $tl['colRow'] = Coordinate::coordinateFromString($tl['cell']); @@ -153,13 +149,10 @@ class Drawing extends WriterPart * Write drawings to XML format. * * @param XMLWriter $objWriter XML Writer - * @param BaseDrawing $pDrawing * @param int $pRelationId * @param null|int $hlinkClickId - * - * @throws WriterException */ - public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1, $hlinkClickId = null) + public function writeDrawing(XMLWriter $objWriter, BaseDrawing $pDrawing, $pRelationId = -1, $hlinkClickId = null): void { if ($pRelationId >= 0) { // xdr:oneCellAnchor @@ -287,10 +280,6 @@ class Drawing extends WriterPart /** * Write VML header/footer images to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet - * - * @throws WriterException - * * @return string XML Output */ public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet) @@ -440,7 +429,7 @@ class Drawing extends WriterPart * @param string $pReference Reference * @param HeaderFooterDrawing $pImage Image */ - private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $pReference, HeaderFooterDrawing $pImage) + private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $pReference, HeaderFooterDrawing $pImage): void { // Calculate object id preg_match('{(\d+)}', md5($pReference), $m); @@ -477,8 +466,6 @@ class Drawing extends WriterPart /** * Get an array of all drawings. * - * @param Spreadsheet $spreadsheet - * * @return \PhpOffice\PhpSpreadsheet\Worksheet\Drawing[] All drawings in PhpSpreadsheet */ public function allDrawings(Spreadsheet $spreadsheet) @@ -502,10 +489,9 @@ class Drawing extends WriterPart } /** - * @param XMLWriter $objWriter * @param null|int $hlinkClickId */ - private function writeHyperLinkDrawing(XMLWriter $objWriter, $hlinkClickId) + private function writeHyperLinkDrawing(XMLWriter $objWriter, $hlinkClickId): void { if ($hlinkClickId === null) { return; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index 76c196b4495..79841404bbd 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -12,10 +12,6 @@ class Rels extends WriterPart /** * Write relationships to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws WriterException - * * @return string XML Output */ public function writeRelationships(Spreadsheet $spreadsheet) @@ -87,10 +83,6 @@ class Rels extends WriterPart /** * Write workbook relationships to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws WriterException - * * @return string XML Output */ public function writeWorkbookRelationships(Spreadsheet $spreadsheet) @@ -168,12 +160,9 @@ class Rels extends WriterPart * rId1 - Drawings * rId_hyperlink_x - Hyperlinks * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet * @param int $pWorksheetId * @param bool $includeCharts Flag indicating if we should write charts * - * @throws WriterException - * * @return string XML Output */ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, $pWorksheetId = 1, $includeCharts = false) @@ -194,7 +183,6 @@ class Rels extends WriterPart $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); // Write drawing relationships? - $d = 0; $drawingOriginalIds = []; $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData(); if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) { @@ -208,13 +196,19 @@ class Rels extends WriterPart } if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) { - $relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; - $rId = ++$d; + $rId = 1; + // Use original $relPath to get original $rId. + // Take first. In future can be overwritten. + // (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeDrawings) + reset($drawingOriginalIds); + $relPath = key($drawingOriginalIds); if (isset($drawingOriginalIds[$relPath])) { $rId = (int) (substr($drawingOriginalIds[$relPath], 3)); } + // Generate new $relPath to write drawing relationship + $relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; $this->writeRelationship( $objWriter, $rId, @@ -277,7 +271,7 @@ class Rels extends WriterPart return $objWriter->getData(); } - private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, XMLWriter $objWriter, $relationship, $type) + private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, XMLWriter $objWriter, $relationship, $type): void { $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData(); if (!isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()][$relationship])) { @@ -297,12 +291,9 @@ class Rels extends WriterPart /** * Write drawing relationships to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet * @param int &$chartRef Chart ID * @param bool $includeCharts Flag indicating if we should write charts * - * @throws WriterException - * * @return string XML Output */ public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet, &$chartRef, $includeCharts = false) @@ -326,8 +317,10 @@ class Rels extends WriterPart $i = 1; $iterator = $pWorksheet->getDrawingCollection()->getIterator(); while ($iterator->valid()) { - if ($iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing - || $iterator->current() instanceof MemoryDrawing) { + if ( + $iterator->current() instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing + || $iterator->current() instanceof MemoryDrawing + ) { // Write relationship for image drawing /** @var \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing */ $drawing = $iterator->current(); @@ -368,10 +361,6 @@ class Rels extends WriterPart /** * Write header/footer drawing relationships to XML format. * - * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet - * - * @throws WriterException - * * @return string XML Output */ public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWorksheet) @@ -415,10 +404,8 @@ class Rels extends WriterPart * @param string $pType Relationship type * @param string $pTarget Relationship target * @param string $pTargetMode Relationship target mode - * - * @throws WriterException */ - private function writeRelationship(XMLWriter $objWriter, $pId, $pType, $pTarget, $pTargetMode = '') + private function writeRelationship(XMLWriter $objWriter, $pId, $pType, $pTarget, $pTargetMode = ''): void { if ($pType != '' && $pTarget != '') { // Write relationship @@ -442,8 +429,6 @@ class Rels extends WriterPart * @param \PhpOffice\PhpSpreadsheet\Worksheet\Drawing $drawing * @param $i * - * @throws WriterException - * * @return int */ private function writeDrawingHyperLink($objWriter, $drawing, $i) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php index 8a0cfe34508..8005207cc2f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php @@ -10,10 +10,6 @@ class RelsRibbon extends WriterPart /** * Write relationships for additional objects of custom UI (ribbon). * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeRibbonRelationships(Spreadsheet $spreadsheet) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php index 01ad38de67b..55bcd360df4 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php @@ -10,10 +10,6 @@ class RelsVBA extends WriterPart /** * Write relationships for a signed VBA Project. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeVBARelationships(Spreadsheet $spreadsheet) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index 19604e44867..b0f7d6d4177 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -8,7 +8,6 @@ use PhpOffice\PhpSpreadsheet\RichText\Run; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; class StringTable extends WriterPart { @@ -39,16 +38,20 @@ class StringTable extends WriterPart foreach ($pSheet->getCoordinates() as $coordinate) { $cell = $pSheet->getCell($coordinate); $cellValue = $cell->getValue(); - if (!is_object($cellValue) && + if ( + !is_object($cellValue) && ($cellValue !== null) && $cellValue !== '' && !isset($aFlippedStringTable[$cellValue]) && - ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL)) { + ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) + ) { $aStringTable[] = $cellValue; $aFlippedStringTable[$cellValue] = true; - } elseif ($cellValue instanceof RichText && + } elseif ( + $cellValue instanceof RichText && ($cellValue !== null) && - !isset($aFlippedStringTable[$cellValue->getHashCode()])) { + !isset($aFlippedStringTable[$cellValue->getHashCode()]) + ) { $aStringTable[] = $cellValue; $aFlippedStringTable[$cellValue->getHashCode()] = true; } @@ -62,8 +65,6 @@ class StringTable extends WriterPart * * @param string[] $pStringTable * - * @throws WriterException - * * @return string XML Output */ public function writeStringTable(array $pStringTable) @@ -115,7 +116,7 @@ class StringTable extends WriterPart * @param RichText $pRichText Rich text * @param string $prefix Optional Namespace prefix */ - public function writeRichText(XMLWriter $objWriter, RichText $pRichText, $prefix = null) + public function writeRichText(XMLWriter $objWriter, RichText $pRichText, $prefix = null): void { if ($prefix !== null) { $prefix .= ':'; @@ -198,7 +199,7 @@ class StringTable extends WriterPart * @param RichText|string $pRichText text string or Rich text * @param string $prefix Optional Namespace prefix */ - public function writeRichTextForCharts(XMLWriter $objWriter, $pRichText = null, $prefix = null) + public function writeRichTextForCharts(XMLWriter $objWriter, $pRichText = null, $prefix = null): void { if (!$pRichText instanceof RichText) { $textRun = $pRichText; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php index 16e800e01f0..0c43fbf490d 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php @@ -18,10 +18,6 @@ class Style extends WriterPart /** * Write styles to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeStyles(Spreadsheet $spreadsheet) @@ -153,11 +149,13 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Fill $pFill Fill style */ - private function writeFill(XMLWriter $objWriter, Fill $pFill) + private function writeFill(XMLWriter $objWriter, Fill $pFill): void { // Check if this is a pattern type or gradient type - if ($pFill->getFillType() === Fill::FILL_GRADIENT_LINEAR || - $pFill->getFillType() === Fill::FILL_GRADIENT_PATH) { + if ( + $pFill->getFillType() === Fill::FILL_GRADIENT_LINEAR || + $pFill->getFillType() === Fill::FILL_GRADIENT_PATH + ) { // Gradient fill $this->writeGradientFill($objWriter, $pFill); } elseif ($pFill->getFillType() !== null) { @@ -172,7 +170,7 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Fill $pFill Fill style */ - private function writeGradientFill(XMLWriter $objWriter, Fill $pFill) + private function writeGradientFill(XMLWriter $objWriter, Fill $pFill): void { // fill $objWriter->startElement('fill'); @@ -215,7 +213,7 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Fill $pFill Fill style */ - private function writePatternFill(XMLWriter $objWriter, Fill $pFill) + private function writePatternFill(XMLWriter $objWriter, Fill $pFill): void { // fill $objWriter->startElement('fill'); @@ -252,7 +250,7 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Font $pFont Font style */ - private function writeFont(XMLWriter $objWriter, Font $pFont) + private function writeFont(XMLWriter $objWriter, Font $pFont): void { // font $objWriter->startElement('font'); @@ -331,7 +329,7 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param Borders $pBorders Borders style */ - private function writeBorder(XMLWriter $objWriter, Borders $pBorders) + private function writeBorder(XMLWriter $objWriter, Borders $pBorders): void { // Write border $objWriter->startElement('border'); @@ -369,10 +367,8 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style * @param Spreadsheet $spreadsheet Workbook - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle, Spreadsheet $spreadsheet) + private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle, Spreadsheet $spreadsheet): void { // xf $objWriter->startElement('xf'); @@ -446,7 +442,7 @@ class Style extends WriterPart * @param XMLWriter $objWriter XML Writer * @param \PhpOffice\PhpSpreadsheet\Style\Style $pStyle Style */ - private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle) + private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $pStyle): void { // dxf $objWriter->startElement('dxf'); @@ -485,15 +481,21 @@ class Style extends WriterPart // protection if (($pStyle->getProtection()->getLocked() !== null) || ($pStyle->getProtection()->getHidden() !== null)) { - if ($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT || - $pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT) { + if ( + $pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT || + $pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT + ) { $objWriter->startElement('protection'); - if (($pStyle->getProtection()->getLocked() !== null) && - ($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)) { + if ( + ($pStyle->getProtection()->getLocked() !== null) && + ($pStyle->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT) + ) { $objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false')); } - if (($pStyle->getProtection()->getHidden() !== null) && - ($pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)) { + if ( + ($pStyle->getProtection()->getHidden() !== null) && + ($pStyle->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT) + ) { $objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false')); } $objWriter->endElement(); @@ -510,7 +512,7 @@ class Style extends WriterPart * @param string $pName Element name * @param Border $pBorder Border style */ - private function writeBorderPr(XMLWriter $objWriter, $pName, Border $pBorder) + private function writeBorderPr(XMLWriter $objWriter, $pName, Border $pBorder): void { // Write BorderPr if ($pBorder->getBorderStyle() != Border::BORDER_NONE) { @@ -533,7 +535,7 @@ class Style extends WriterPart * @param NumberFormat $pNumberFormat Number Format * @param int $pId Number Format identifier */ - private function writeNumFmt(XMLWriter $objWriter, NumberFormat $pNumberFormat, $pId = 0) + private function writeNumFmt(XMLWriter $objWriter, NumberFormat $pNumberFormat, $pId = 0): void { // Translate formatcode $formatCode = $pNumberFormat->getFormatCode(); @@ -550,8 +552,6 @@ class Style extends WriterPart /** * Get an array of all styles. * - * @param Spreadsheet $spreadsheet - * * @return \PhpOffice\PhpSpreadsheet\Style\Style[] All styles in PhpSpreadsheet */ public function allStyles(Spreadsheet $spreadsheet) @@ -562,8 +562,6 @@ class Style extends WriterPart /** * Get an array of all conditional styles. * - * @param Spreadsheet $spreadsheet - * * @return Conditional[] All conditional styles in PhpSpreadsheet */ public function allConditionalStyles(Spreadsheet $spreadsheet) @@ -586,8 +584,6 @@ class Style extends WriterPart /** * Get an array of all fills. * - * @param Spreadsheet $spreadsheet - * * @return Fill[] All fills in PhpSpreadsheet */ public function allFills(Spreadsheet $spreadsheet) @@ -618,8 +614,6 @@ class Style extends WriterPart /** * Get an array of all fonts. * - * @param Spreadsheet $spreadsheet - * * @return Font[] All fonts in PhpSpreadsheet */ public function allFonts(Spreadsheet $spreadsheet) @@ -641,8 +635,6 @@ class Style extends WriterPart /** * Get an array of all borders. * - * @param Spreadsheet $spreadsheet - * * @return Borders[] All borders in PhpSpreadsheet */ public function allBorders(Spreadsheet $spreadsheet) @@ -664,8 +656,6 @@ class Style extends WriterPart /** * Get an array of all number formats. * - * @param Spreadsheet $spreadsheet - * * @return NumberFormat[] All number formats in PhpSpreadsheet */ public function allNumberFormats(Spreadsheet $spreadsheet) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php index f5f8dc0752d..3a47be7fb6f 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php @@ -5,11 +5,6 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Spreadsheet; -/** - * @category PhpSpreadsheet - * - * @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) - */ class Theme extends WriterPart { /** @@ -109,10 +104,6 @@ class Theme extends WriterPart /** * Write theme to XML format. * - * @param Spreadsheet $spreadsheet - * - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * * @return string XML Output */ public function writeTheme(Spreadsheet $spreadsheet) diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php index fd936748ad0..0a20ea9d9f5 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php @@ -2,24 +2,19 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx; -use PhpOffice\PhpSpreadsheet\Cell\Coordinate; -use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx\DefinedNames as DefinedNamesWriter; class Workbook extends WriterPart { /** * Write workbook to XML format. * - * @param Spreadsheet $spreadsheet * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing * - * @throws WriterException - * * @return string XML Output */ public function writeWorkbook(Spreadsheet $spreadsheet, $recalcRequired = false) @@ -58,7 +53,7 @@ class Workbook extends WriterPart $this->writeSheets($objWriter, $spreadsheet); // definedNames - $this->writeDefinedNames($objWriter, $spreadsheet); + (new DefinedNamesWriter($objWriter, $spreadsheet))->write(); // calcPr $this->writeCalcPr($objWriter, $recalcRequired); @@ -74,7 +69,7 @@ class Workbook extends WriterPart * * @param XMLWriter $objWriter XML Writer */ - private function writeFileVersion(XMLWriter $objWriter) + private function writeFileVersion(XMLWriter $objWriter): void { $objWriter->startElement('fileVersion'); $objWriter->writeAttribute('appName', 'xl'); @@ -89,7 +84,7 @@ class Workbook extends WriterPart * * @param XMLWriter $objWriter XML Writer */ - private function writeWorkbookPr(XMLWriter $objWriter) + private function writeWorkbookPr(XMLWriter $objWriter): void { $objWriter->startElement('workbookPr'); @@ -106,9 +101,8 @@ class Workbook extends WriterPart * Write BookViews. * * @param XMLWriter $objWriter XML Writer - * @param Spreadsheet $spreadsheet */ - private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet) + private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet): void { // bookViews $objWriter->startElement('bookViews'); @@ -135,9 +129,8 @@ class Workbook extends WriterPart * Write WorkbookProtection. * * @param XMLWriter $objWriter XML Writer - * @param Spreadsheet $spreadsheet */ - private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet) + private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet): void { if ($spreadsheet->getSecurity()->isSecurityEnabled()) { $objWriter->startElement('workbookProtection'); @@ -163,7 +156,7 @@ class Workbook extends WriterPart * @param XMLWriter $objWriter XML Writer * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing */ - private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true) + private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true): void { $objWriter->startElement('calcPr'); @@ -184,11 +177,8 @@ class Workbook extends WriterPart * Write sheets. * * @param XMLWriter $objWriter XML Writer - * @param Spreadsheet $spreadsheet - * - * @throws WriterException */ - private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet) + private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet): void { // Write sheets $objWriter->startElement('sheets'); @@ -215,10 +205,8 @@ class Workbook extends WriterPart * @param int $pSheetId Sheet id * @param int $pRelId Relationship ID * @param string $sheetState Sheet state (visible, hidden, veryHidden) - * - * @throws WriterException */ - private function writeSheet(XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible') + private function writeSheet(XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible'): void { if ($pSheetname != '') { // Write sheet @@ -234,193 +222,4 @@ class Workbook extends WriterPart throw new WriterException('Invalid parameters passed.'); } } - - /** - * Write Defined Names. - * - * @param XMLWriter $objWriter XML Writer - * @param Spreadsheet $spreadsheet - * - * @throws WriterException - */ - private function writeDefinedNames(XMLWriter $objWriter, Spreadsheet $spreadsheet) - { - // Write defined names - $objWriter->startElement('definedNames'); - - // Named ranges - if (count($spreadsheet->getNamedRanges()) > 0) { - // Named ranges - $this->writeNamedRanges($objWriter, $spreadsheet); - } - - // Other defined names - $sheetCount = $spreadsheet->getSheetCount(); - for ($i = 0; $i < $sheetCount; ++$i) { - // definedName for autoFilter - $this->writeDefinedNameForAutofilter($objWriter, $spreadsheet->getSheet($i), $i); - - // definedName for Print_Titles - $this->writeDefinedNameForPrintTitles($objWriter, $spreadsheet->getSheet($i), $i); - - // definedName for Print_Area - $this->writeDefinedNameForPrintArea($objWriter, $spreadsheet->getSheet($i), $i); - } - - $objWriter->endElement(); - } - - /** - * Write named ranges. - * - * @param XMLWriter $objWriter XML Writer - * @param Spreadsheet $spreadsheet - * - * @throws WriterException - */ - private function writeNamedRanges(XMLWriter $objWriter, Spreadsheet $spreadsheet) - { - // Loop named ranges - $namedRanges = $spreadsheet->getNamedRanges(); - foreach ($namedRanges as $namedRange) { - $this->writeDefinedNameForNamedRange($objWriter, $namedRange); - } - } - - /** - * Write Defined Name for named range. - * - * @param XMLWriter $objWriter XML Writer - * @param NamedRange $pNamedRange - */ - private function writeDefinedNameForNamedRange(XMLWriter $objWriter, NamedRange $pNamedRange) - { - // definedName for named range - $objWriter->startElement('definedName'); - $objWriter->writeAttribute('name', $pNamedRange->getName()); - if ($pNamedRange->getLocalOnly()) { - $objWriter->writeAttribute('localSheetId', $pNamedRange->getScope()->getParent()->getIndex($pNamedRange->getScope())); - } - - // Create absolute coordinate and write as raw text - $range = Coordinate::splitRange($pNamedRange->getRange()); - $iMax = count($range); - for ($i = 0; $i < $iMax; ++$i) { - $range[$i][0] = '\'' . str_replace("'", "''", $pNamedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteReference($range[$i][0]); - if (isset($range[$i][1])) { - $range[$i][1] = Coordinate::absoluteReference($range[$i][1]); - } - } - $range = Coordinate::buildRange($range); - - $objWriter->writeRawData($range); - - $objWriter->endElement(); - } - - /** - * Write Defined Name for autoFilter. - * - * @param XMLWriter $objWriter XML Writer - * @param Worksheet $pSheet - * @param int $pSheetId - */ - private function writeDefinedNameForAutofilter(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0) - { - // definedName for autoFilter - $autoFilterRange = $pSheet->getAutoFilter()->getRange(); - if (!empty($autoFilterRange)) { - $objWriter->startElement('definedName'); - $objWriter->writeAttribute('name', '_xlnm._FilterDatabase'); - $objWriter->writeAttribute('localSheetId', $pSheetId); - $objWriter->writeAttribute('hidden', '1'); - - // Create absolute coordinate and write as raw text - $range = Coordinate::splitRange($autoFilterRange); - $range = $range[0]; - // Strip any worksheet ref so we can make the cell ref absolute - [$ws, $range[0]] = Worksheet::extractSheetTitle($range[0], true); - - $range[0] = Coordinate::absoluteCoordinate($range[0]); - $range[1] = Coordinate::absoluteCoordinate($range[1]); - $range = implode(':', $range); - - $objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range); - - $objWriter->endElement(); - } - } - - /** - * Write Defined Name for PrintTitles. - * - * @param XMLWriter $objWriter XML Writer - * @param Worksheet $pSheet - * @param int $pSheetId - */ - private function writeDefinedNameForPrintTitles(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0) - { - // definedName for PrintTitles - if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { - $objWriter->startElement('definedName'); - $objWriter->writeAttribute('name', '_xlnm.Print_Titles'); - $objWriter->writeAttribute('localSheetId', $pSheetId); - - // Setting string - $settingString = ''; - - // Columns to repeat - if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { - $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft(); - - $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; - } - - // Rows to repeat - if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { - if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { - $settingString .= ','; - } - - $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop(); - - $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; - } - - $objWriter->writeRawData($settingString); - - $objWriter->endElement(); - } - } - - /** - * Write Defined Name for PrintTitles. - * - * @param XMLWriter $objWriter XML Writer - * @param Worksheet $pSheet - * @param int $pSheetId - */ - private function writeDefinedNameForPrintArea(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0) - { - // definedName for PrintArea - if ($pSheet->getPageSetup()->isPrintAreaSet()) { - $objWriter->startElement('definedName'); - $objWriter->writeAttribute('name', '_xlnm.Print_Area'); - $objWriter->writeAttribute('localSheetId', $pSheetId); - - // Print area - $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea()); - - $chunks = []; - foreach ($printArea as $printAreaRect) { - $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]); - $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]); - $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect); - } - - $objWriter->writeRawData(implode(',', $chunks)); - - $objWriter->endElement(); - } - } } diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index f460c0f12cd..8faa7ae259e 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -12,24 +12,15 @@ use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; use PhpOffice\PhpSpreadsheet\Worksheet\SheetView; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as PhpspreadsheetWorksheet; -use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; -/** - * @category PhpSpreadsheet - * - * @copyright Copyright (c) 2006 - 2015 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet) - */ class Worksheet extends WriterPart { /** * Write worksheet to XML format. * - * @param PhpspreadsheetWorksheet $pSheet * @param string[] $pStringTable * @param bool $includeCharts Flag indicating if we should write charts * - * @throws WriterException - * * @return string XML Output */ public function writeWorksheet(PhpspreadsheetWorksheet $pSheet, $pStringTable = null, $includeCharts = false) @@ -135,7 +126,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeSheetPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeSheetPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // sheetPr $objWriter->startElement('sheetPr'); @@ -181,7 +172,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeDimension(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeDimension(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // dimension $objWriter->startElement('dimension'); @@ -194,10 +185,8 @@ class Worksheet extends WriterPart * * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet - * - * @throws WriterException */ - private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // sheetViews $objWriter->startElement('sheetViews'); @@ -262,8 +251,6 @@ class Worksheet extends WriterPart --$ySplit; $topLeftCell = $pSheet->getTopLeftCell(); - $activeCell = $topLeftCell; - $sqref = $topLeftCell; // pane $pane = 'topRight'; @@ -313,7 +300,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeSheetFormatPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeSheetFormatPr(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // sheetFormatPr $objWriter->startElement('sheetFormatPr'); @@ -327,8 +314,10 @@ class Worksheet extends WriterPart } // Set Zero Height row - if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() === '1' || - strtolower((string) $pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true') { + if ( + (string) $pSheet->getDefaultRowDimension()->getZeroHeight() === '1' || + strtolower((string) $pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true' + ) { $objWriter->writeAttribute('zeroHeight', '1'); } @@ -364,7 +353,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeCols(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeCols(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // cols if (count($pSheet->getColumnDimensions()) > 0) { @@ -428,43 +417,99 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeSheetProtection(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeSheetProtection(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // sheetProtection $objWriter->startElement('sheetProtection'); - if ($pSheet->getProtection()->getPassword() !== '') { - $objWriter->writeAttribute('password', $pSheet->getProtection()->getPassword()); + $protection = $pSheet->getProtection(); + + if ($protection->getAlgorithm()) { + $objWriter->writeAttribute('algorithmName', $protection->getAlgorithm()); + $objWriter->writeAttribute('hashValue', $protection->getPassword()); + $objWriter->writeAttribute('saltValue', $protection->getSalt()); + $objWriter->writeAttribute('spinCount', $protection->getSpinCount()); + } elseif ($protection->getPassword() !== '') { + $objWriter->writeAttribute('password', $protection->getPassword()); } - $objWriter->writeAttribute('sheet', ($pSheet->getProtection()->getSheet() ? 'true' : 'false')); - $objWriter->writeAttribute('objects', ($pSheet->getProtection()->getObjects() ? 'true' : 'false')); - $objWriter->writeAttribute('scenarios', ($pSheet->getProtection()->getScenarios() ? 'true' : 'false')); - $objWriter->writeAttribute('formatCells', ($pSheet->getProtection()->getFormatCells() ? 'true' : 'false')); - $objWriter->writeAttribute('formatColumns', ($pSheet->getProtection()->getFormatColumns() ? 'true' : 'false')); - $objWriter->writeAttribute('formatRows', ($pSheet->getProtection()->getFormatRows() ? 'true' : 'false')); - $objWriter->writeAttribute('insertColumns', ($pSheet->getProtection()->getInsertColumns() ? 'true' : 'false')); - $objWriter->writeAttribute('insertRows', ($pSheet->getProtection()->getInsertRows() ? 'true' : 'false')); - $objWriter->writeAttribute('insertHyperlinks', ($pSheet->getProtection()->getInsertHyperlinks() ? 'true' : 'false')); - $objWriter->writeAttribute('deleteColumns', ($pSheet->getProtection()->getDeleteColumns() ? 'true' : 'false')); - $objWriter->writeAttribute('deleteRows', ($pSheet->getProtection()->getDeleteRows() ? 'true' : 'false')); - $objWriter->writeAttribute('selectLockedCells', ($pSheet->getProtection()->getSelectLockedCells() ? 'true' : 'false')); - $objWriter->writeAttribute('sort', ($pSheet->getProtection()->getSort() ? 'true' : 'false')); - $objWriter->writeAttribute('autoFilter', ($pSheet->getProtection()->getAutoFilter() ? 'true' : 'false')); - $objWriter->writeAttribute('pivotTables', ($pSheet->getProtection()->getPivotTables() ? 'true' : 'false')); - $objWriter->writeAttribute('selectUnlockedCells', ($pSheet->getProtection()->getSelectUnlockedCells() ? 'true' : 'false')); + $objWriter->writeAttribute('sheet', ($protection->getSheet() ? 'true' : 'false')); + $objWriter->writeAttribute('objects', ($protection->getObjects() ? 'true' : 'false')); + $objWriter->writeAttribute('scenarios', ($protection->getScenarios() ? 'true' : 'false')); + $objWriter->writeAttribute('formatCells', ($protection->getFormatCells() ? 'true' : 'false')); + $objWriter->writeAttribute('formatColumns', ($protection->getFormatColumns() ? 'true' : 'false')); + $objWriter->writeAttribute('formatRows', ($protection->getFormatRows() ? 'true' : 'false')); + $objWriter->writeAttribute('insertColumns', ($protection->getInsertColumns() ? 'true' : 'false')); + $objWriter->writeAttribute('insertRows', ($protection->getInsertRows() ? 'true' : 'false')); + $objWriter->writeAttribute('insertHyperlinks', ($protection->getInsertHyperlinks() ? 'true' : 'false')); + $objWriter->writeAttribute('deleteColumns', ($protection->getDeleteColumns() ? 'true' : 'false')); + $objWriter->writeAttribute('deleteRows', ($protection->getDeleteRows() ? 'true' : 'false')); + $objWriter->writeAttribute('selectLockedCells', ($protection->getSelectLockedCells() ? 'true' : 'false')); + $objWriter->writeAttribute('sort', ($protection->getSort() ? 'true' : 'false')); + $objWriter->writeAttribute('autoFilter', ($protection->getAutoFilter() ? 'true' : 'false')); + $objWriter->writeAttribute('pivotTables', ($protection->getPivotTables() ? 'true' : 'false')); + $objWriter->writeAttribute('selectUnlockedCells', ($protection->getSelectUnlockedCells() ? 'true' : 'false')); $objWriter->endElement(); } + private static function writeAttributeIf(XMLWriter $objWriter, $condition, string $attr, string $val): void + { + if ($condition) { + $objWriter->writeAttribute($attr, $val); + } + } + + private static function writeElementIf(XMLWriter $objWriter, $condition, string $attr, string $val): void + { + if ($condition) { + $objWriter->writeElement($attr, $val); + } + } + + private static function writeOtherCondElements(XMLWriter $objWriter, Conditional $conditional, string $cellCoordinate): void + { + if ( + $conditional->getConditionType() == Conditional::CONDITION_CELLIS + || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION + ) { + foreach ($conditional->getConditions() as $formula) { + // Formula + $objWriter->writeElement('formula', Xlfn::addXlfn($formula)); + } + } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) { + // formula copied from ms xlsx xml source file + $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0'); + } elseif ($conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSBLANKS) { + // formula copied from ms xlsx xml source file + $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))>0'); + } + } + + private static function writeTextCondElements(XMLWriter $objWriter, Conditional $conditional, string $cellCoordinate): void + { + $txt = $conditional->getText(); + if ($txt !== null) { + $objWriter->writeAttribute('text', $txt); + if ($conditional->getOperatorType() == Conditional::OPERATOR_CONTAINSTEXT) { + $objWriter->writeElement('formula', 'NOT(ISERROR(SEARCH("' . $txt . '",' . $cellCoordinate . ')))'); + } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_BEGINSWITH) { + $objWriter->writeElement('formula', 'LEFT(' . $cellCoordinate . ',' . strlen($txt) . ')="' . $txt . '"'); + } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_ENDSWITH) { + $objWriter->writeElement('formula', 'RIGHT(' . $cellCoordinate . ',' . strlen($txt) . ')="' . $txt . '"'); + } elseif ($conditional->getOperatorType() == Conditional::OPERATOR_NOTCONTAINS) { + $objWriter->writeElement('formula', 'ISERROR(SEARCH("' . $txt . '",' . $cellCoordinate . '))'); + } + } + } + /** * Write ConditionalFormatting. * * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet - * - * @throws WriterException */ - private function writeConditionalFormatting(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeConditionalFormatting(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // Conditional id $id = 1; @@ -487,46 +532,20 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('dxfId', $this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode())); $objWriter->writeAttribute('priority', $id++); - if (($conditional->getConditionType() == Conditional::CONDITION_CELLIS || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) - && $conditional->getOperatorType() != Conditional::OPERATOR_NONE) { - $objWriter->writeAttribute('operator', $conditional->getOperatorType()); - } + self::writeAttributeif( + $objWriter, + ($conditional->getConditionType() == Conditional::CONDITION_CELLIS || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) + && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, + 'operator', + $conditional->getOperatorType() + ); - if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - && $conditional->getText() !== null) { - $objWriter->writeAttribute('text', $conditional->getText()); - } + self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1'); - if ($conditional->getStopIfTrue()) { - $objWriter->writeAttribute('stopIfTrue', '1'); - } - - if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - && $conditional->getOperatorType() == Conditional::OPERATOR_CONTAINSTEXT - && $conditional->getText() !== null) { - $objWriter->writeElement('formula', 'NOT(ISERROR(SEARCH("' . $conditional->getText() . '",' . $cellCoordinate . ')))'); - } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - && $conditional->getOperatorType() == Conditional::OPERATOR_BEGINSWITH - && $conditional->getText() !== null) { - $objWriter->writeElement('formula', 'LEFT(' . $cellCoordinate . ',' . strlen($conditional->getText()) . ')="' . $conditional->getText() . '"'); - } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - && $conditional->getOperatorType() == Conditional::OPERATOR_ENDSWITH - && $conditional->getText() !== null) { - $objWriter->writeElement('formula', 'RIGHT(' . $cellCoordinate . ',' . strlen($conditional->getText()) . ')="' . $conditional->getText() . '"'); - } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - && $conditional->getOperatorType() == Conditional::OPERATOR_NOTCONTAINS - && $conditional->getText() !== null) { - $objWriter->writeElement('formula', 'ISERROR(SEARCH("' . $conditional->getText() . '",' . $cellCoordinate . '))'); - } elseif ($conditional->getConditionType() == Conditional::CONDITION_CELLIS - || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - || $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) { - foreach ($conditional->getConditions() as $formula) { - // Formula - $objWriter->writeElement('formula', $formula); - } - } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) { - // formula copied from ms xlsx xml source file - $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0'); + if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) { + self::writeTextCondElements($objWriter, $conditional, $cellCoordinate); + } else { + self::writeOtherCondElements($objWriter, $conditional, $cellCoordinate); } $objWriter->endElement(); @@ -543,7 +562,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // Datavalidation collection $dataValidationCollection = $pSheet->getDataValidationCollection(); @@ -609,7 +628,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeHyperlinks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeHyperlinks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // Hyperlink collection $hyperlinkCollection = $pSheet->getHyperlinkCollection(); @@ -650,7 +669,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeProtectedRanges(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeProtectedRanges(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { if (count($pSheet->getProtectedCells()) > 0) { // protectedRanges @@ -678,7 +697,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeMergeCells(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeMergeCells(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { if (count($pSheet->getMergeCells()) > 0) { // mergeCells @@ -702,7 +721,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writePrintOptions(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writePrintOptions(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // printOptions $objWriter->startElement('printOptions'); @@ -727,7 +746,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writePageMargins(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writePageMargins(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // pageMargins $objWriter->startElement('pageMargins'); @@ -746,7 +765,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeAutoFilter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeAutoFilter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { $autoFilterRange = $pSheet->getAutoFilter()->getRange(); if (!empty($autoFilterRange)) { @@ -776,9 +795,11 @@ class Worksheet extends WriterPart } foreach ($rules as $rule) { - if (($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER) && + if ( + ($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER) && ($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_EQUAL) && - ($rule->getValue() === '')) { + ($rule->getValue() === '') + ) { // Filter rule for Blanks $objWriter->writeAttribute('blank', 1); } elseif ($rule->getRuleType() === Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER) { @@ -836,7 +857,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writePageSetup(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writePageSetup(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // pageSetup $objWriter->startElement('pageSetup'); @@ -860,6 +881,7 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('firstPageNumber', $pSheet->getPageSetup()->getFirstPageNumber()); $objWriter->writeAttribute('useFirstPageNumber', '1'); } + $objWriter->writeAttribute('pageOrder', $pSheet->getPageSetup()->getPageOrder()); $getUnparsedLoadedData = $pSheet->getParent()->getUnparsedLoadedData(); if (isset($getUnparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId'])) { @@ -875,7 +897,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeHeaderFooter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeHeaderFooter(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // headerFooter $objWriter->startElement('headerFooter'); @@ -899,7 +921,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeBreaks(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // Get row and column breaks $aRowBreaks = []; @@ -955,10 +977,8 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet * @param string[] $pStringTable String table - * - * @throws WriterException */ - private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, array $pStringTable) + private function writeSheetData(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, array $pStringTable): void { // Flipped stringtable, for faster index searching $aFlippedStringTable = $this->getParentWriter()->getWriterPart('stringtable')->flipStringTable($pStringTable); @@ -1036,17 +1056,110 @@ class Worksheet extends WriterPart $objWriter->endElement(); } + /** + * @param RichText|string $cellValue + */ + private function writeCellInlineStr(XMLWriter $objWriter, string $mappedType, $cellValue): void + { + $objWriter->writeAttribute('t', $mappedType); + if (!$cellValue instanceof RichText) { + $objWriter->writeElement('t', StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue))); + } elseif ($cellValue instanceof RichText) { + $objWriter->startElement('is'); + $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue); + $objWriter->endElement(); + } + } + + /** + * @param RichText|string $cellValue + * @param string[] $pFlippedStringTable + */ + private function writeCellString(XMLWriter $objWriter, string $mappedType, $cellValue, array $pFlippedStringTable): void + { + $objWriter->writeAttribute('t', $mappedType); + if (!$cellValue instanceof RichText) { + self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue] ?? ''); + } else { + $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]); + } + } + + /** + * @param float|int $cellValue + */ + private function writeCellNumeric(XMLWriter $objWriter, $cellValue): void + { + //force a decimal to be written if the type is float + if (is_float($cellValue)) { + // force point as decimal separator in case current locale uses comma + $cellValue = str_replace(',', '.', (string) $cellValue); + if (strpos($cellValue, '.') === false) { + $cellValue = $cellValue . '.0'; + } + } + $objWriter->writeElement('v', $cellValue); + } + + private function writeCellBoolean(XMLWriter $objWriter, string $mappedType, bool $cellValue): void + { + $objWriter->writeAttribute('t', $mappedType); + $objWriter->writeElement('v', $cellValue ? '1' : '0'); + } + + private function writeCellError(XMLWriter $objWriter, string $mappedType, string $cellValue, string $formulaerr = '#NULL!'): void + { + $objWriter->writeAttribute('t', $mappedType); + $cellIsFormula = substr($cellValue, 0, 1) === '='; + self::writeElementIf($objWriter, $cellIsFormula, 'f', Xlfn::addXlfnStripEquals($cellValue)); + $objWriter->writeElement('v', $cellIsFormula ? $formulaerr : $cellValue); + } + + private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell $pCell): void + { + $calculatedValue = $this->getParentWriter()->getPreCalculateFormulas() ? $pCell->getCalculatedValue() : $cellValue; + if (is_string($calculatedValue)) { + if (\PhpOffice\PhpSpreadsheet\Calculation\Functions::isError($calculatedValue)) { + $this->writeCellError($objWriter, 'e', $cellValue, $calculatedValue); + + return; + } + $objWriter->writeAttribute('t', 'str'); + } elseif (is_bool($calculatedValue)) { + $objWriter->writeAttribute('t', 'b'); + } + // array values are not yet supported + //$attributes = $pCell->getFormulaAttributes(); + //if (($attributes['t'] ?? null) === 'array') { + // $objWriter->startElement('f'); + // $objWriter->writeAttribute('t', 'array'); + // $objWriter->writeAttribute('ref', $pCellAddress); + // $objWriter->writeAttribute('aca', '1'); + // $objWriter->writeAttribute('ca', '1'); + // $objWriter->text(substr($cellValue, 1)); + // $objWriter->endElement(); + //} else { + // $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue)); + //} + $objWriter->writeElement('f', Xlfn::addXlfnStripEquals($cellValue)); + self::writeElementIf( + $objWriter, + $this->getParentWriter()->getOffice2003Compatibility() === false, + 'v', + ($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#') + ? StringHelper::formatNumber($calculatedValue) : '0' + ); + } + /** * Write Cell. * * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet - * @param Cell $pCellAddress Cell Address + * @param string $pCellAddress Cell Address * @param string[] $pFlippedStringTable String table (flipped), for faster index searching - * - * @throws WriterException */ - private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $pCellAddress, array $pFlippedStringTable) + private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, string $pCellAddress, array $pFlippedStringTable): void { // Cell $pCell = $pSheet->getCell($pCellAddress); @@ -1054,9 +1167,8 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('r', $pCellAddress); // Sheet styles - if ($pCell->getXfIndex() != '') { - $objWriter->writeAttribute('s', $pCell->getXfIndex()); - } + $xfi = $pCell->getXfIndex(); + self::writeAttributeIf($objWriter, $xfi, 's', $xfi); // If cell value is supplied, write cell value $cellValue = $pCell->getValue(); @@ -1064,101 +1176,30 @@ class Worksheet extends WriterPart // Map type $mappedType = $pCell->getDataType(); - // Write data type depending on its type - switch (strtolower($mappedType)) { - case 'inlinestr': // Inline string - case 's': // String - case 'b': // Boolean - $objWriter->writeAttribute('t', $mappedType); - - break; - case 'f': // Formula - $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ? - $pCell->getCalculatedValue() : $cellValue; - if (is_string($calculatedValue)) { - $objWriter->writeAttribute('t', 'str'); - } elseif (is_bool($calculatedValue)) { - $objWriter->writeAttribute('t', 'b'); - } - - break; - case 'e': // Error - $objWriter->writeAttribute('t', $mappedType); - } - // Write data depending on its type switch (strtolower($mappedType)) { case 'inlinestr': // Inline string - if (!$cellValue instanceof RichText) { - $objWriter->writeElement('t', StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue))); - } elseif ($cellValue instanceof RichText) { - $objWriter->startElement('is'); - $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue); - $objWriter->endElement(); - } + $this->writeCellInlineStr($objWriter, $mappedType, $cellValue); break; case 's': // String - if (!$cellValue instanceof RichText) { - if (isset($pFlippedStringTable[$cellValue])) { - $objWriter->writeElement('v', $pFlippedStringTable[$cellValue]); - } - } elseif ($cellValue instanceof RichText) { - $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]); - } + $this->writeCellString($objWriter, $mappedType, $cellValue, $pFlippedStringTable); break; case 'f': // Formula - $attributes = $pCell->getFormulaAttributes(); - if (($attributes['t'] ?? null) === 'array') { - $objWriter->startElement('f'); - $objWriter->writeAttribute('t', 'array'); - $objWriter->writeAttribute('ref', $pCellAddress); - $objWriter->writeAttribute('aca', '1'); - $objWriter->writeAttribute('ca', '1'); - $objWriter->text(substr($cellValue, 1)); - $objWriter->endElement(); - } else { - $objWriter->writeElement('f', substr($cellValue, 1)); - } - if ($this->getParentWriter()->getOffice2003Compatibility() === false) { - if ($this->getParentWriter()->getPreCalculateFormulas()) { - if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#') { - $objWriter->writeElement('v', StringHelper::formatNumber($calculatedValue)); - } else { - $objWriter->writeElement('v', '0'); - } - } else { - $objWriter->writeElement('v', '0'); - } - } + $this->writeCellFormula($objWriter, $cellValue, $pCell); break; case 'n': // Numeric - //force a decimal to be written if the type is float - if (is_float($cellValue)) { - $cellValue = (string) $cellValue; - if (strpos($cellValue, '.') === false) { - $cellValue = $cellValue . '.0'; - } - } - // force point as decimal separator in case current locale uses comma - $objWriter->writeElement('v', str_replace(',', '.', $cellValue)); + $this->writeCellNumeric($objWriter, $cellValue); break; case 'b': // Boolean - $objWriter->writeElement('v', ($cellValue ? '1' : '0')); + $this->writeCellBoolean($objWriter, $mappedType, $cellValue); break; case 'e': // Error - if (substr($cellValue, 0, 1) === '=') { - $objWriter->writeElement('f', substr($cellValue, 1)); - $objWriter->writeElement('v', substr($cellValue, 1)); - } else { - $objWriter->writeElement('v', $cellValue); - } - - break; + $this->writeCellError($objWriter, $mappedType, $cellValue); } } @@ -1172,7 +1213,7 @@ class Worksheet extends WriterPart * @param PhpspreadsheetWorksheet $pSheet Worksheet * @param bool $includeCharts Flag indicating if we should include drawing details for charts */ - private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false) + private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false): void { $unparsedLoadedData = $pSheet->getParent()->getUnparsedLoadedData(); $hasUnparsedDrawing = isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']); @@ -1188,6 +1229,7 @@ class Worksheet extends WriterPart if (isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) { $drawingOriginalIds = $unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']; // take first. In future can be overriten + // (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels::writeWorksheetRelationships) $rId = reset($drawingOriginalIds); } @@ -1201,7 +1243,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // If sheet contains comments, add the relationships if (count($pSheet->getComments()) > 0) { @@ -1217,7 +1259,7 @@ class Worksheet extends WriterPart * @param XMLWriter $objWriter XML Writer * @param PhpspreadsheetWorksheet $pSheet Worksheet */ - private function writeLegacyDrawingHF(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeLegacyDrawingHF(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { // If sheet contains images, add the relationships if (count($pSheet->getHeaderFooter()->getImages()) > 0) { @@ -1227,7 +1269,7 @@ class Worksheet extends WriterPart } } - private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet): void { if (empty($pSheet->getParent()->getUnparsedLoadedData()['sheets'][$pSheet->getCodeName()]['AlternateContents'])) { return; diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php index 7119512ce41..a9137dfc858 100644 --- a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php @@ -25,8 +25,6 @@ abstract class WriterPart /** * Set parent Xlsx object. - * - * @param Xlsx $pWriter */ public function __construct(Xlsx $pWriter) { diff --git a/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php new file mode 100644 index 00000000000..8f7c07e83ae --- /dev/null +++ b/lib/phpspreadsheet/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php @@ -0,0 +1,159 @@ +=7.0.0", + "psr/http-message": "^1.0" + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/phpspreadsheet/vendor/psr/http-factory/src/RequestFactoryInterface.php b/lib/phpspreadsheet/vendor/psr/http-factory/src/RequestFactoryInterface.php new file mode 100644 index 00000000000..cb39a08bf5c --- /dev/null +++ b/lib/phpspreadsheet/vendor/psr/http-factory/src/RequestFactoryInterface.php @@ -0,0 +1,18 @@ +=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/phpspreadsheet/vendor/psr/http-message/src/MessageInterface.php b/lib/phpspreadsheet/vendor/psr/http-message/src/MessageInterface.php new file mode 100644 index 00000000000..dd46e5ec81e --- /dev/null +++ b/lib/phpspreadsheet/vendor/psr/http-message/src/MessageInterface.php @@ -0,0 +1,187 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. + */ + public function getHeaders(); + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($name); + + /** + * Retrieves a message header value by the given case-insensitive name. + * + * This method returns an array of all the header values of the given + * case-insensitive header name. + * + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader($name); + + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. + * + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. + * + * If the header does not appear in the message, this method MUST return + * an empty string. + * + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. + */ + public function getHeaderLine($name); + + /** + * Return an instance with the provided value replacing the specified header. + * + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. + * + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withHeader($name, $value); + + /** + * Return an instance with the specified header appended with the given value. + * + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new header and/or value. + * + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader($name, $value); + + /** + * Return an instance without the specified header. + * + * Header resolution MUST be done without case-sensitivity. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. + * + * @param string $name Case-insensitive header field name to remove. + * @return static + */ + public function withoutHeader($name); + + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody(); + + /** + * Return an instance with the specified message body. + * + * The body MUST be a StreamInterface object. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return static + * @throws \InvalidArgumentException When the body is not valid. + */ + public function withBody(StreamInterface $body); +} diff --git a/lib/phpspreadsheet/vendor/psr/http-message/src/RequestInterface.php b/lib/phpspreadsheet/vendor/psr/http-message/src/RequestInterface.php new file mode 100644 index 00000000000..a96d4fd6366 --- /dev/null +++ b/lib/phpspreadsheet/vendor/psr/http-message/src/RequestInterface.php @@ -0,0 +1,129 @@ +getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array + */ + public function getQueryParams(); + + /** + * Return an instance with the specified query string arguments. + * + * These values SHOULD remain immutable over the course of the incoming + * request. They MAY be injected during instantiation, such as from PHP's + * $_GET superglobal, or MAY be derived from some other value such as the + * URI. In cases where the arguments are parsed from the URI, the data + * MUST be compatible with what PHP's parse_str() would return for + * purposes of how duplicate query parameters are handled, and how nested + * sets are handled. + * + * Setting query string arguments MUST NOT change the URI stored by the + * request, nor the values in the server params. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated query string arguments. + * + * @param array $query Array of query string arguments, typically from + * $_GET. + * @return static + */ + public function withQueryParams(array $query); + + /** + * Retrieve normalized file upload data. + * + * This method returns upload metadata in a normalized tree, with each leaf + * an instance of Psr\Http\Message\UploadedFileInterface. + * + * These values MAY be prepared from $_FILES or the message body during + * instantiation, or MAY be injected via withUploadedFiles(). + * + * @return array An array tree of UploadedFileInterface instances; an empty + * array MUST be returned if no data is present. + */ + public function getUploadedFiles(); + + /** + * Create a new instance with the specified uploaded files. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param array $uploadedFiles An array tree of UploadedFileInterface instances. + * @return static + * @throws \InvalidArgumentException if an invalid structure is provided. + */ + public function withUploadedFiles(array $uploadedFiles); + + /** + * Retrieve any parameters provided in the request body. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, this method MUST + * return the contents of $_POST. + * + * Otherwise, this method may return any results of deserializing + * the request body content; as parsing returns structured content, the + * potential types MUST be arrays or objects only. A null value indicates + * the absence of body content. + * + * @return null|array|object The deserialized body parameters, if any. + * These will typically be an array or object. + */ + public function getParsedBody(); + + /** + * Return an instance with the specified body parameters. + * + * These MAY be injected during instantiation. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, use this method + * ONLY to inject the contents of $_POST. + * + * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of + * deserializing the request body content. Deserialization/parsing returns + * structured data, and, as such, this method ONLY accepts arrays or objects, + * or a null value if nothing was available to parse. + * + * As an example, if content negotiation determines that the request data + * is a JSON payload, this method could be used to create a request + * instance with the deserialized parameters. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param null|array|object $data The deserialized body data. This will + * typically be in an array or object. + * @return static + * @throws \InvalidArgumentException if an unsupported argument type is + * provided. + */ + public function withParsedBody($data); + + /** + * Retrieve attributes derived from the request. + * + * The request "attributes" may be used to allow injection of any + * parameters derived from the request: e.g., the results of path + * match operations; the results of decrypting cookies; the results of + * deserializing non-form-encoded message bodies; etc. Attributes + * will be application and request specific, and CAN be mutable. + * + * @return array Attributes derived from the request. + */ + public function getAttributes(); + + /** + * Retrieve a single derived request attribute. + * + * Retrieves a single derived request attribute as described in + * getAttributes(). If the attribute has not been previously set, returns + * the default value as provided. + * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed + */ + public function getAttribute($name, $default = null); + + /** + * Return an instance with the specified derived request attribute. + * + * This method allows setting a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return static + */ + public function withAttribute($name, $value); + + /** + * Return an instance that removes the specified derived request attribute. + * + * This method allows removing a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @return static + */ + public function withoutAttribute($name); +} diff --git a/lib/phpspreadsheet/vendor/psr/http-message/src/StreamInterface.php b/lib/phpspreadsheet/vendor/psr/http-message/src/StreamInterface.php new file mode 100644 index 00000000000..f68f391269b --- /dev/null +++ b/lib/phpspreadsheet/vendor/psr/http-message/src/StreamInterface.php @@ -0,0 +1,158 @@ + + * [user-info@]host[:port] + * + * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority(); + + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo(); + + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost(); + + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort(); + + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath(); + + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery(); + + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment(); + + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return static A new instance with the specified scheme. + * @throws \InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme($scheme); + + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return static A new instance with the specified user information. + */ + public function withUserInfo($user, $password = null); + + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return static A new instance with the specified host. + * @throws \InvalidArgumentException for invalid hostnames. + */ + public function withHost($host); + + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return static A new instance with the specified port. + * @throws \InvalidArgumentException for invalid ports. + */ + public function withPort($port); + + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return static A new instance with the specified path. + * @throws \InvalidArgumentException for invalid paths. + */ + public function withPath($path); + + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return static A new instance with the specified query string. + * @throws \InvalidArgumentException for invalid query strings. + */ + public function withQuery($query); + + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return static A new instance with the specified fragment. + */ + public function withFragment($fragment); + + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString(); +} diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/LICENSE b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 00000000000..4cd8bdd3007 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +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/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Mbstring.php b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 00000000000..8b3b7589312 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,869 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_convert_case - Perform case folding on a string + * - mb_detect_encoding - Detect character encoding + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_str_split - Convert a string to an array + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within another + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + public const MB_CASE_FOLD = \PHP_INT_MAX; + + private static $encodingList = ['ASCII', 'UTF-8']; + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) + { + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); + } + + public static function mb_decode_numericentity($s, $convmap, $encoding = null) + { + if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { + return false; + } + + if (null !== $encoding && !is_scalar($encoding)) { + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return ''; // Instead of null (cf. mb_encode_numericentity). + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $cnt = floor(\count($convmap) / 4) * 4; + + for ($i = 0; $i < $cnt; $i += 4) { + // collector_decode_htmlnumericentity ignores $convmap[$i + 3] + $convmap[$i] += $convmap[$i + 2]; + $convmap[$i + 1] += $convmap[$i + 2]; + } + + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; + for ($i = 0; $i < $cnt; $i += 4) { + if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { + return self::mb_chr($c - $convmap[$i + 2]); + } + } + + return $m[0]; + }, $s); + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) + { + if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { + return false; + } + + if (null !== $encoding && !is_scalar($encoding)) { + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; // Instead of '' (cf. mb_decode_numericentity). + } + + if (null !== $is_hex && !is_scalar($is_hex)) { + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; + + $cnt = floor(\count($convmap) / 4) * 4; + $i = 0; + $len = \strlen($s); + $result = ''; + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + $c = self::mb_ord($uchr); + + for ($j = 0; $j < $cnt; $j += 4) { + if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { + $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; + continue 2; + } + } + $result .= $uchr; + } + + if (null === $encoding) { + return $result; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $result); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + if (\MB_CASE_TITLE == $mode) { + static $titleRegexp = null; + if (null === $titleRegexp) { + $titleRegexp = self::getData('titleCaseRegexp'); + } + $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); + } else { + if (\MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; + + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = \strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $normalizedEncoding = self::getEncoding($encoding); + + if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + self::$internalEncoding = $normalizedEncoding; + + return true; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($normalizedLang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $normalizedLang; + + return true; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); + } + + public static function mb_list_encodings() + { + return ['UTF-8']; + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return ['utf8']; + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!\is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!\is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + // no break + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return \strlen($s); + } + + return @iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } + + $needle = (string) $needle; + if ('' === $needle) { + if (80000 > \PHP_VERSION_ID) { + trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); + + return false; + } + + return 0; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + if (0 > $offset += self::mb_strlen($needle)) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + } + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = '' !== $needle || 80000 > \PHP_VERSION_ID + ? iconv_strrpos($haystack, $needle, $encoding) + : self::mb_strlen($haystack, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_str_split($string, $split_length = 1, $encoding = null) + { + if (null !== $string && !is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); + + return null; + } + + if (1 > $split_length = (int) $split_length) { + if (80000 > \PHP_VERSION_ID) { + trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); + return false; + } + + throw new \ValueError('Argument #2 ($length) must be greater than 0'); + } + + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } + + if ('UTF-8' === $encoding = self::getEncoding($encoding)) { + $rx = '/('; + while (65535 < $split_length) { + $rx .= '.{65535}'; + $split_length -= 65535; + } + $rx .= '.{'.$split_length.'})/us'; + + return preg_split($rx, $string, null, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + } + + $result = []; + $length = mb_strlen($string, $encoding); + + for ($i = 0; $i < $length; $i += $split_length) { + $result[] = mb_substr($string, $i, $split_length, $encoding); + } + + return $result; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (null === $c) { + return 'none'; + } + if (0 === strcasecmp($c, 'none')) { + return true; + } + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return (string) substr($s, $start, null === $length ? 2147483647 : $length); + } + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return (string) iconv_substr($s, $start, $length, $encoding); + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + $pos = strrpos($haystack, $needle); + } else { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + } + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = [ + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ]; + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = \chr($code); + } elseif (0x800 > $code) { + $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + if (1 === \strlen($s)) { + return \ord($s); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback(array $m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= \chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case(array $s) + { + return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + if ('UTF-8' === $encoding) { + return 'UTF-8'; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/README.md b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 00000000000..4efb599d81f --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](https://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 00000000000..a22eca57bd9 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1397 @@ + 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + 'À' => 'à', + 'Á' => 'á', + 'Â' => 'â', + 'Ã' => 'ã', + 'Ä' => 'ä', + 'Å' => 'å', + 'Æ' => 'æ', + 'Ç' => 'ç', + 'È' => 'è', + 'É' => 'é', + 'Ê' => 'ê', + 'Ë' => 'ë', + 'Ì' => 'ì', + 'Í' => 'í', + 'Î' => 'î', + 'Ï' => 'ï', + 'Ð' => 'ð', + 'Ñ' => 'ñ', + 'Ò' => 'ò', + 'Ó' => 'ó', + 'Ô' => 'ô', + 'Õ' => 'õ', + 'Ö' => 'ö', + 'Ø' => 'ø', + 'Ù' => 'ù', + 'Ú' => 'ú', + 'Û' => 'û', + 'Ü' => 'ü', + 'Ý' => 'ý', + 'Þ' => 'þ', + 'Ā' => 'ā', + 'Ă' => 'ă', + 'Ą' => 'ą', + 'Ć' => 'ć', + 'Ĉ' => 'ĉ', + 'Ċ' => 'ċ', + 'Č' => 'č', + 'Ď' => 'ď', + 'Đ' => 'đ', + 'Ē' => 'ē', + 'Ĕ' => 'ĕ', + 'Ė' => 'ė', + 'Ę' => 'ę', + 'Ě' => 'ě', + 'Ĝ' => 'ĝ', + 'Ğ' => 'ğ', + 'Ġ' => 'ġ', + 'Ģ' => 'ģ', + 'Ĥ' => 'ĥ', + 'Ħ' => 'ħ', + 'Ĩ' => 'ĩ', + 'Ī' => 'ī', + 'Ĭ' => 'ĭ', + 'Į' => 'į', + 'İ' => 'i', + 'IJ' => 'ij', + 'Ĵ' => 'ĵ', + 'Ķ' => 'ķ', + 'Ĺ' => 'ĺ', + 'Ļ' => 'ļ', + 'Ľ' => 'ľ', + 'Ŀ' => 'ŀ', + 'Ł' => 'ł', + 'Ń' => 'ń', + 'Ņ' => 'ņ', + 'Ň' => 'ň', + 'Ŋ' => 'ŋ', + 'Ō' => 'ō', + 'Ŏ' => 'ŏ', + 'Ő' => 'ő', + 'Œ' => 'œ', + 'Ŕ' => 'ŕ', + 'Ŗ' => 'ŗ', + 'Ř' => 'ř', + 'Ś' => 'ś', + 'Ŝ' => 'ŝ', + 'Ş' => 'ş', + 'Š' => 'š', + 'Ţ' => 'ţ', + 'Ť' => 'ť', + 'Ŧ' => 'ŧ', + 'Ũ' => 'ũ', + 'Ū' => 'ū', + 'Ŭ' => 'ŭ', + 'Ů' => 'ů', + 'Ű' => 'ű', + 'Ų' => 'ų', + 'Ŵ' => 'ŵ', + 'Ŷ' => 'ŷ', + 'Ÿ' => 'ÿ', + 'Ź' => 'ź', + 'Ż' => 'ż', + 'Ž' => 'ž', + 'Ɓ' => 'ɓ', + 'Ƃ' => 'ƃ', + 'Ƅ' => 'ƅ', + 'Ɔ' => 'ɔ', + 'Ƈ' => 'ƈ', + 'Ɖ' => 'ɖ', + 'Ɗ' => 'ɗ', + 'Ƌ' => 'ƌ', + 'Ǝ' => 'ǝ', + 'Ə' => 'ə', + 'Ɛ' => 'ɛ', + 'Ƒ' => 'ƒ', + 'Ɠ' => 'ɠ', + 'Ɣ' => 'ɣ', + 'Ɩ' => 'ɩ', + 'Ɨ' => 'ɨ', + 'Ƙ' => 'ƙ', + 'Ɯ' => 'ɯ', + 'Ɲ' => 'ɲ', + 'Ɵ' => 'ɵ', + 'Ơ' => 'ơ', + 'Ƣ' => 'ƣ', + 'Ƥ' => 'ƥ', + 'Ʀ' => 'ʀ', + 'Ƨ' => 'ƨ', + 'Ʃ' => 'ʃ', + 'Ƭ' => 'ƭ', + 'Ʈ' => 'ʈ', + 'Ư' => 'ư', + 'Ʊ' => 'ʊ', + 'Ʋ' => 'ʋ', + 'Ƴ' => 'ƴ', + 'Ƶ' => 'ƶ', + 'Ʒ' => 'ʒ', + 'Ƹ' => 'ƹ', + 'Ƽ' => 'ƽ', + 'DŽ' => 'dž', + 'Dž' => 'dž', + 'LJ' => 'lj', + 'Lj' => 'lj', + 'NJ' => 'nj', + 'Nj' => 'nj', + 'Ǎ' => 'ǎ', + 'Ǐ' => 'ǐ', + 'Ǒ' => 'ǒ', + 'Ǔ' => 'ǔ', + 'Ǖ' => 'ǖ', + 'Ǘ' => 'ǘ', + 'Ǚ' => 'ǚ', + 'Ǜ' => 'ǜ', + 'Ǟ' => 'ǟ', + 'Ǡ' => 'ǡ', + 'Ǣ' => 'ǣ', + 'Ǥ' => 'ǥ', + 'Ǧ' => 'ǧ', + 'Ǩ' => 'ǩ', + 'Ǫ' => 'ǫ', + 'Ǭ' => 'ǭ', + 'Ǯ' => 'ǯ', + 'DZ' => 'dz', + 'Dz' => 'dz', + 'Ǵ' => 'ǵ', + 'Ƕ' => 'ƕ', + 'Ƿ' => 'ƿ', + 'Ǹ' => 'ǹ', + 'Ǻ' => 'ǻ', + 'Ǽ' => 'ǽ', + 'Ǿ' => 'ǿ', + 'Ȁ' => 'ȁ', + 'Ȃ' => 'ȃ', + 'Ȅ' => 'ȅ', + 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', + 'Ȋ' => 'ȋ', + 'Ȍ' => 'ȍ', + 'Ȏ' => 'ȏ', + 'Ȑ' => 'ȑ', + 'Ȓ' => 'ȓ', + 'Ȕ' => 'ȕ', + 'Ȗ' => 'ȗ', + 'Ș' => 'ș', + 'Ț' => 'ț', + 'Ȝ' => 'ȝ', + 'Ȟ' => 'ȟ', + 'Ƞ' => 'ƞ', + 'Ȣ' => 'ȣ', + 'Ȥ' => 'ȥ', + 'Ȧ' => 'ȧ', + 'Ȩ' => 'ȩ', + 'Ȫ' => 'ȫ', + 'Ȭ' => 'ȭ', + 'Ȯ' => 'ȯ', + 'Ȱ' => 'ȱ', + 'Ȳ' => 'ȳ', + 'Ⱥ' => 'ⱥ', + 'Ȼ' => 'ȼ', + 'Ƚ' => 'ƚ', + 'Ⱦ' => 'ⱦ', + 'Ɂ' => 'ɂ', + 'Ƀ' => 'ƀ', + 'Ʉ' => 'ʉ', + 'Ʌ' => 'ʌ', + 'Ɇ' => 'ɇ', + 'Ɉ' => 'ɉ', + 'Ɋ' => 'ɋ', + 'Ɍ' => 'ɍ', + 'Ɏ' => 'ɏ', + 'Ͱ' => 'ͱ', + 'Ͳ' => 'ͳ', + 'Ͷ' => 'ͷ', + 'Ϳ' => 'ϳ', + 'Ά' => 'ά', + 'Έ' => 'έ', + 'Ή' => 'ή', + 'Ί' => 'ί', + 'Ό' => 'ό', + 'Ύ' => 'ύ', + 'Ώ' => 'ώ', + 'Α' => 'α', + 'Β' => 'β', + 'Γ' => 'γ', + 'Δ' => 'δ', + 'Ε' => 'ε', + 'Ζ' => 'ζ', + 'Η' => 'η', + 'Θ' => 'θ', + 'Ι' => 'ι', + 'Κ' => 'κ', + 'Λ' => 'λ', + 'Μ' => 'μ', + 'Ν' => 'ν', + 'Ξ' => 'ξ', + 'Ο' => 'ο', + 'Π' => 'π', + 'Ρ' => 'ρ', + 'Σ' => 'σ', + 'Τ' => 'τ', + 'Υ' => 'υ', + 'Φ' => 'φ', + 'Χ' => 'χ', + 'Ψ' => 'ψ', + 'Ω' => 'ω', + 'Ϊ' => 'ϊ', + 'Ϋ' => 'ϋ', + 'Ϗ' => 'ϗ', + 'Ϙ' => 'ϙ', + 'Ϛ' => 'ϛ', + 'Ϝ' => 'ϝ', + 'Ϟ' => 'ϟ', + 'Ϡ' => 'ϡ', + 'Ϣ' => 'ϣ', + 'Ϥ' => 'ϥ', + 'Ϧ' => 'ϧ', + 'Ϩ' => 'ϩ', + 'Ϫ' => 'ϫ', + 'Ϭ' => 'ϭ', + 'Ϯ' => 'ϯ', + 'ϴ' => 'θ', + 'Ϸ' => 'ϸ', + 'Ϲ' => 'ϲ', + 'Ϻ' => 'ϻ', + 'Ͻ' => 'ͻ', + 'Ͼ' => 'ͼ', + 'Ͽ' => 'ͽ', + 'Ѐ' => 'ѐ', + 'Ё' => 'ё', + 'Ђ' => 'ђ', + 'Ѓ' => 'ѓ', + 'Є' => 'є', + 'Ѕ' => 'ѕ', + 'І' => 'і', + 'Ї' => 'ї', + 'Ј' => 'ј', + 'Љ' => 'љ', + 'Њ' => 'њ', + 'Ћ' => 'ћ', + 'Ќ' => 'ќ', + 'Ѝ' => 'ѝ', + 'Ў' => 'ў', + 'Џ' => 'џ', + 'А' => 'а', + 'Б' => 'б', + 'В' => 'в', + 'Г' => 'г', + 'Д' => 'д', + 'Е' => 'е', + 'Ж' => 'ж', + 'З' => 'з', + 'И' => 'и', + 'Й' => 'й', + 'К' => 'к', + 'Л' => 'л', + 'М' => 'м', + 'Н' => 'н', + 'О' => 'о', + 'П' => 'п', + 'Р' => 'р', + 'С' => 'с', + 'Т' => 'т', + 'У' => 'у', + 'Ф' => 'ф', + 'Х' => 'х', + 'Ц' => 'ц', + 'Ч' => 'ч', + 'Ш' => 'ш', + 'Щ' => 'щ', + 'Ъ' => 'ъ', + 'Ы' => 'ы', + 'Ь' => 'ь', + 'Э' => 'э', + 'Ю' => 'ю', + 'Я' => 'я', + 'Ѡ' => 'ѡ', + 'Ѣ' => 'ѣ', + 'Ѥ' => 'ѥ', + 'Ѧ' => 'ѧ', + 'Ѩ' => 'ѩ', + 'Ѫ' => 'ѫ', + 'Ѭ' => 'ѭ', + 'Ѯ' => 'ѯ', + 'Ѱ' => 'ѱ', + 'Ѳ' => 'ѳ', + 'Ѵ' => 'ѵ', + 'Ѷ' => 'ѷ', + 'Ѹ' => 'ѹ', + 'Ѻ' => 'ѻ', + 'Ѽ' => 'ѽ', + 'Ѿ' => 'ѿ', + 'Ҁ' => 'ҁ', + 'Ҋ' => 'ҋ', + 'Ҍ' => 'ҍ', + 'Ҏ' => 'ҏ', + 'Ґ' => 'ґ', + 'Ғ' => 'ғ', + 'Ҕ' => 'ҕ', + 'Җ' => 'җ', + 'Ҙ' => 'ҙ', + 'Қ' => 'қ', + 'Ҝ' => 'ҝ', + 'Ҟ' => 'ҟ', + 'Ҡ' => 'ҡ', + 'Ң' => 'ң', + 'Ҥ' => 'ҥ', + 'Ҧ' => 'ҧ', + 'Ҩ' => 'ҩ', + 'Ҫ' => 'ҫ', + 'Ҭ' => 'ҭ', + 'Ү' => 'ү', + 'Ұ' => 'ұ', + 'Ҳ' => 'ҳ', + 'Ҵ' => 'ҵ', + 'Ҷ' => 'ҷ', + 'Ҹ' => 'ҹ', + 'Һ' => 'һ', + 'Ҽ' => 'ҽ', + 'Ҿ' => 'ҿ', + 'Ӏ' => 'ӏ', + 'Ӂ' => 'ӂ', + 'Ӄ' => 'ӄ', + 'Ӆ' => 'ӆ', + 'Ӈ' => 'ӈ', + 'Ӊ' => 'ӊ', + 'Ӌ' => 'ӌ', + 'Ӎ' => 'ӎ', + 'Ӑ' => 'ӑ', + 'Ӓ' => 'ӓ', + 'Ӕ' => 'ӕ', + 'Ӗ' => 'ӗ', + 'Ә' => 'ә', + 'Ӛ' => 'ӛ', + 'Ӝ' => 'ӝ', + 'Ӟ' => 'ӟ', + 'Ӡ' => 'ӡ', + 'Ӣ' => 'ӣ', + 'Ӥ' => 'ӥ', + 'Ӧ' => 'ӧ', + 'Ө' => 'ө', + 'Ӫ' => 'ӫ', + 'Ӭ' => 'ӭ', + 'Ӯ' => 'ӯ', + 'Ӱ' => 'ӱ', + 'Ӳ' => 'ӳ', + 'Ӵ' => 'ӵ', + 'Ӷ' => 'ӷ', + 'Ӹ' => 'ӹ', + 'Ӻ' => 'ӻ', + 'Ӽ' => 'ӽ', + 'Ӿ' => 'ӿ', + 'Ԁ' => 'ԁ', + 'Ԃ' => 'ԃ', + 'Ԅ' => 'ԅ', + 'Ԇ' => 'ԇ', + 'Ԉ' => 'ԉ', + 'Ԋ' => 'ԋ', + 'Ԍ' => 'ԍ', + 'Ԏ' => 'ԏ', + 'Ԑ' => 'ԑ', + 'Ԓ' => 'ԓ', + 'Ԕ' => 'ԕ', + 'Ԗ' => 'ԗ', + 'Ԙ' => 'ԙ', + 'Ԛ' => 'ԛ', + 'Ԝ' => 'ԝ', + 'Ԟ' => 'ԟ', + 'Ԡ' => 'ԡ', + 'Ԣ' => 'ԣ', + 'Ԥ' => 'ԥ', + 'Ԧ' => 'ԧ', + 'Ԩ' => 'ԩ', + 'Ԫ' => 'ԫ', + 'Ԭ' => 'ԭ', + 'Ԯ' => 'ԯ', + 'Ա' => 'ա', + 'Բ' => 'բ', + 'Գ' => 'գ', + 'Դ' => 'դ', + 'Ե' => 'ե', + 'Զ' => 'զ', + 'Է' => 'է', + 'Ը' => 'ը', + 'Թ' => 'թ', + 'Ժ' => 'ժ', + 'Ի' => 'ի', + 'Լ' => 'լ', + 'Խ' => 'խ', + 'Ծ' => 'ծ', + 'Կ' => 'կ', + 'Հ' => 'հ', + 'Ձ' => 'ձ', + 'Ղ' => 'ղ', + 'Ճ' => 'ճ', + 'Մ' => 'մ', + 'Յ' => 'յ', + 'Ն' => 'ն', + 'Շ' => 'շ', + 'Ո' => 'ո', + 'Չ' => 'չ', + 'Պ' => 'պ', + 'Ջ' => 'ջ', + 'Ռ' => 'ռ', + 'Ս' => 'ս', + 'Վ' => 'վ', + 'Տ' => 'տ', + 'Ր' => 'ր', + 'Ց' => 'ց', + 'Ւ' => 'ւ', + 'Փ' => 'փ', + 'Ք' => 'ք', + 'Օ' => 'օ', + 'Ֆ' => 'ֆ', + 'Ⴀ' => 'ⴀ', + 'Ⴁ' => 'ⴁ', + 'Ⴂ' => 'ⴂ', + 'Ⴃ' => 'ⴃ', + 'Ⴄ' => 'ⴄ', + 'Ⴅ' => 'ⴅ', + 'Ⴆ' => 'ⴆ', + 'Ⴇ' => 'ⴇ', + 'Ⴈ' => 'ⴈ', + 'Ⴉ' => 'ⴉ', + 'Ⴊ' => 'ⴊ', + 'Ⴋ' => 'ⴋ', + 'Ⴌ' => 'ⴌ', + 'Ⴍ' => 'ⴍ', + 'Ⴎ' => 'ⴎ', + 'Ⴏ' => 'ⴏ', + 'Ⴐ' => 'ⴐ', + 'Ⴑ' => 'ⴑ', + 'Ⴒ' => 'ⴒ', + 'Ⴓ' => 'ⴓ', + 'Ⴔ' => 'ⴔ', + 'Ⴕ' => 'ⴕ', + 'Ⴖ' => 'ⴖ', + 'Ⴗ' => 'ⴗ', + 'Ⴘ' => 'ⴘ', + 'Ⴙ' => 'ⴙ', + 'Ⴚ' => 'ⴚ', + 'Ⴛ' => 'ⴛ', + 'Ⴜ' => 'ⴜ', + 'Ⴝ' => 'ⴝ', + 'Ⴞ' => 'ⴞ', + 'Ⴟ' => 'ⴟ', + 'Ⴠ' => 'ⴠ', + 'Ⴡ' => 'ⴡ', + 'Ⴢ' => 'ⴢ', + 'Ⴣ' => 'ⴣ', + 'Ⴤ' => 'ⴤ', + 'Ⴥ' => 'ⴥ', + 'Ⴧ' => 'ⴧ', + 'Ⴭ' => 'ⴭ', + 'Ꭰ' => 'ꭰ', + 'Ꭱ' => 'ꭱ', + 'Ꭲ' => 'ꭲ', + 'Ꭳ' => 'ꭳ', + 'Ꭴ' => 'ꭴ', + 'Ꭵ' => 'ꭵ', + 'Ꭶ' => 'ꭶ', + 'Ꭷ' => 'ꭷ', + 'Ꭸ' => 'ꭸ', + 'Ꭹ' => 'ꭹ', + 'Ꭺ' => 'ꭺ', + 'Ꭻ' => 'ꭻ', + 'Ꭼ' => 'ꭼ', + 'Ꭽ' => 'ꭽ', + 'Ꭾ' => 'ꭾ', + 'Ꭿ' => 'ꭿ', + 'Ꮀ' => 'ꮀ', + 'Ꮁ' => 'ꮁ', + 'Ꮂ' => 'ꮂ', + 'Ꮃ' => 'ꮃ', + 'Ꮄ' => 'ꮄ', + 'Ꮅ' => 'ꮅ', + 'Ꮆ' => 'ꮆ', + 'Ꮇ' => 'ꮇ', + 'Ꮈ' => 'ꮈ', + 'Ꮉ' => 'ꮉ', + 'Ꮊ' => 'ꮊ', + 'Ꮋ' => 'ꮋ', + 'Ꮌ' => 'ꮌ', + 'Ꮍ' => 'ꮍ', + 'Ꮎ' => 'ꮎ', + 'Ꮏ' => 'ꮏ', + 'Ꮐ' => 'ꮐ', + 'Ꮑ' => 'ꮑ', + 'Ꮒ' => 'ꮒ', + 'Ꮓ' => 'ꮓ', + 'Ꮔ' => 'ꮔ', + 'Ꮕ' => 'ꮕ', + 'Ꮖ' => 'ꮖ', + 'Ꮗ' => 'ꮗ', + 'Ꮘ' => 'ꮘ', + 'Ꮙ' => 'ꮙ', + 'Ꮚ' => 'ꮚ', + 'Ꮛ' => 'ꮛ', + 'Ꮜ' => 'ꮜ', + 'Ꮝ' => 'ꮝ', + 'Ꮞ' => 'ꮞ', + 'Ꮟ' => 'ꮟ', + 'Ꮠ' => 'ꮠ', + 'Ꮡ' => 'ꮡ', + 'Ꮢ' => 'ꮢ', + 'Ꮣ' => 'ꮣ', + 'Ꮤ' => 'ꮤ', + 'Ꮥ' => 'ꮥ', + 'Ꮦ' => 'ꮦ', + 'Ꮧ' => 'ꮧ', + 'Ꮨ' => 'ꮨ', + 'Ꮩ' => 'ꮩ', + 'Ꮪ' => 'ꮪ', + 'Ꮫ' => 'ꮫ', + 'Ꮬ' => 'ꮬ', + 'Ꮭ' => 'ꮭ', + 'Ꮮ' => 'ꮮ', + 'Ꮯ' => 'ꮯ', + 'Ꮰ' => 'ꮰ', + 'Ꮱ' => 'ꮱ', + 'Ꮲ' => 'ꮲ', + 'Ꮳ' => 'ꮳ', + 'Ꮴ' => 'ꮴ', + 'Ꮵ' => 'ꮵ', + 'Ꮶ' => 'ꮶ', + 'Ꮷ' => 'ꮷ', + 'Ꮸ' => 'ꮸ', + 'Ꮹ' => 'ꮹ', + 'Ꮺ' => 'ꮺ', + 'Ꮻ' => 'ꮻ', + 'Ꮼ' => 'ꮼ', + 'Ꮽ' => 'ꮽ', + 'Ꮾ' => 'ꮾ', + 'Ꮿ' => 'ꮿ', + 'Ᏸ' => 'ᏸ', + 'Ᏹ' => 'ᏹ', + 'Ᏺ' => 'ᏺ', + 'Ᏻ' => 'ᏻ', + 'Ᏼ' => 'ᏼ', + 'Ᏽ' => 'ᏽ', + 'Ა' => 'ა', + 'Ბ' => 'ბ', + 'Გ' => 'გ', + 'Დ' => 'დ', + 'Ე' => 'ე', + 'Ვ' => 'ვ', + 'Ზ' => 'ზ', + 'Თ' => 'თ', + 'Ი' => 'ი', + 'Კ' => 'კ', + 'Ლ' => 'ლ', + 'Მ' => 'მ', + 'Ნ' => 'ნ', + 'Ო' => 'ო', + 'Პ' => 'პ', + 'Ჟ' => 'ჟ', + 'Რ' => 'რ', + 'Ს' => 'ს', + 'Ტ' => 'ტ', + 'Უ' => 'უ', + 'Ფ' => 'ფ', + 'Ქ' => 'ქ', + 'Ღ' => 'ღ', + 'Ყ' => 'ყ', + 'Შ' => 'შ', + 'Ჩ' => 'ჩ', + 'Ც' => 'ც', + 'Ძ' => 'ძ', + 'Წ' => 'წ', + 'Ჭ' => 'ჭ', + 'Ხ' => 'ხ', + 'Ჯ' => 'ჯ', + 'Ჰ' => 'ჰ', + 'Ჱ' => 'ჱ', + 'Ჲ' => 'ჲ', + 'Ჳ' => 'ჳ', + 'Ჴ' => 'ჴ', + 'Ჵ' => 'ჵ', + 'Ჶ' => 'ჶ', + 'Ჷ' => 'ჷ', + 'Ჸ' => 'ჸ', + 'Ჹ' => 'ჹ', + 'Ჺ' => 'ჺ', + 'Ჽ' => 'ჽ', + 'Ჾ' => 'ჾ', + 'Ჿ' => 'ჿ', + 'Ḁ' => 'ḁ', + 'Ḃ' => 'ḃ', + 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', + 'Ḉ' => 'ḉ', + 'Ḋ' => 'ḋ', + 'Ḍ' => 'ḍ', + 'Ḏ' => 'ḏ', + 'Ḑ' => 'ḑ', + 'Ḓ' => 'ḓ', + 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', + 'Ḙ' => 'ḙ', + 'Ḛ' => 'ḛ', + 'Ḝ' => 'ḝ', + 'Ḟ' => 'ḟ', + 'Ḡ' => 'ḡ', + 'Ḣ' => 'ḣ', + 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', + 'Ḩ' => 'ḩ', + 'Ḫ' => 'ḫ', + 'Ḭ' => 'ḭ', + 'Ḯ' => 'ḯ', + 'Ḱ' => 'ḱ', + 'Ḳ' => 'ḳ', + 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', + 'Ḹ' => 'ḹ', + 'Ḻ' => 'ḻ', + 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', + 'Ṁ' => 'ṁ', + 'Ṃ' => 'ṃ', + 'Ṅ' => 'ṅ', + 'Ṇ' => 'ṇ', + 'Ṉ' => 'ṉ', + 'Ṋ' => 'ṋ', + 'Ṍ' => 'ṍ', + 'Ṏ' => 'ṏ', + 'Ṑ' => 'ṑ', + 'Ṓ' => 'ṓ', + 'Ṕ' => 'ṕ', + 'Ṗ' => 'ṗ', + 'Ṙ' => 'ṙ', + 'Ṛ' => 'ṛ', + 'Ṝ' => 'ṝ', + 'Ṟ' => 'ṟ', + 'Ṡ' => 'ṡ', + 'Ṣ' => 'ṣ', + 'Ṥ' => 'ṥ', + 'Ṧ' => 'ṧ', + 'Ṩ' => 'ṩ', + 'Ṫ' => 'ṫ', + 'Ṭ' => 'ṭ', + 'Ṯ' => 'ṯ', + 'Ṱ' => 'ṱ', + 'Ṳ' => 'ṳ', + 'Ṵ' => 'ṵ', + 'Ṷ' => 'ṷ', + 'Ṹ' => 'ṹ', + 'Ṻ' => 'ṻ', + 'Ṽ' => 'ṽ', + 'Ṿ' => 'ṿ', + 'Ẁ' => 'ẁ', + 'Ẃ' => 'ẃ', + 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', + 'Ẉ' => 'ẉ', + 'Ẋ' => 'ẋ', + 'Ẍ' => 'ẍ', + 'Ẏ' => 'ẏ', + 'Ẑ' => 'ẑ', + 'Ẓ' => 'ẓ', + 'Ẕ' => 'ẕ', + 'ẞ' => 'ß', + 'Ạ' => 'ạ', + 'Ả' => 'ả', + 'Ấ' => 'ấ', + 'Ầ' => 'ầ', + 'Ẩ' => 'ẩ', + 'Ẫ' => 'ẫ', + 'Ậ' => 'ậ', + 'Ắ' => 'ắ', + 'Ằ' => 'ằ', + 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', + 'Ặ' => 'ặ', + 'Ẹ' => 'ẹ', + 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', + 'Ế' => 'ế', + 'Ề' => 'ề', + 'Ể' => 'ể', + 'Ễ' => 'ễ', + 'Ệ' => 'ệ', + 'Ỉ' => 'ỉ', + 'Ị' => 'ị', + 'Ọ' => 'ọ', + 'Ỏ' => 'ỏ', + 'Ố' => 'ố', + 'Ồ' => 'ồ', + 'Ổ' => 'ổ', + 'Ỗ' => 'ỗ', + 'Ộ' => 'ộ', + 'Ớ' => 'ớ', + 'Ờ' => 'ờ', + 'Ở' => 'ở', + 'Ỡ' => 'ỡ', + 'Ợ' => 'ợ', + 'Ụ' => 'ụ', + 'Ủ' => 'ủ', + 'Ứ' => 'ứ', + 'Ừ' => 'ừ', + 'Ử' => 'ử', + 'Ữ' => 'ữ', + 'Ự' => 'ự', + 'Ỳ' => 'ỳ', + 'Ỵ' => 'ỵ', + 'Ỷ' => 'ỷ', + 'Ỹ' => 'ỹ', + 'Ỻ' => 'ỻ', + 'Ỽ' => 'ỽ', + 'Ỿ' => 'ỿ', + 'Ἀ' => 'ἀ', + 'Ἁ' => 'ἁ', + 'Ἂ' => 'ἂ', + 'Ἃ' => 'ἃ', + 'Ἄ' => 'ἄ', + 'Ἅ' => 'ἅ', + 'Ἆ' => 'ἆ', + 'Ἇ' => 'ἇ', + 'Ἐ' => 'ἐ', + 'Ἑ' => 'ἑ', + 'Ἒ' => 'ἒ', + 'Ἓ' => 'ἓ', + 'Ἔ' => 'ἔ', + 'Ἕ' => 'ἕ', + 'Ἠ' => 'ἠ', + 'Ἡ' => 'ἡ', + 'Ἢ' => 'ἢ', + 'Ἣ' => 'ἣ', + 'Ἤ' => 'ἤ', + 'Ἥ' => 'ἥ', + 'Ἦ' => 'ἦ', + 'Ἧ' => 'ἧ', + 'Ἰ' => 'ἰ', + 'Ἱ' => 'ἱ', + 'Ἲ' => 'ἲ', + 'Ἳ' => 'ἳ', + 'Ἴ' => 'ἴ', + 'Ἵ' => 'ἵ', + 'Ἶ' => 'ἶ', + 'Ἷ' => 'ἷ', + 'Ὀ' => 'ὀ', + 'Ὁ' => 'ὁ', + 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', + 'Ὄ' => 'ὄ', + 'Ὅ' => 'ὅ', + 'Ὑ' => 'ὑ', + 'Ὓ' => 'ὓ', + 'Ὕ' => 'ὕ', + 'Ὗ' => 'ὗ', + 'Ὠ' => 'ὠ', + 'Ὡ' => 'ὡ', + 'Ὢ' => 'ὢ', + 'Ὣ' => 'ὣ', + 'Ὤ' => 'ὤ', + 'Ὥ' => 'ὥ', + 'Ὦ' => 'ὦ', + 'Ὧ' => 'ὧ', + 'ᾈ' => 'ᾀ', + 'ᾉ' => 'ᾁ', + 'ᾊ' => 'ᾂ', + 'ᾋ' => 'ᾃ', + 'ᾌ' => 'ᾄ', + 'ᾍ' => 'ᾅ', + 'ᾎ' => 'ᾆ', + 'ᾏ' => 'ᾇ', + 'ᾘ' => 'ᾐ', + 'ᾙ' => 'ᾑ', + 'ᾚ' => 'ᾒ', + 'ᾛ' => 'ᾓ', + 'ᾜ' => 'ᾔ', + 'ᾝ' => 'ᾕ', + 'ᾞ' => 'ᾖ', + 'ᾟ' => 'ᾗ', + 'ᾨ' => 'ᾠ', + 'ᾩ' => 'ᾡ', + 'ᾪ' => 'ᾢ', + 'ᾫ' => 'ᾣ', + 'ᾬ' => 'ᾤ', + 'ᾭ' => 'ᾥ', + 'ᾮ' => 'ᾦ', + 'ᾯ' => 'ᾧ', + 'Ᾰ' => 'ᾰ', + 'Ᾱ' => 'ᾱ', + 'Ὰ' => 'ὰ', + 'Ά' => 'ά', + 'ᾼ' => 'ᾳ', + 'Ὲ' => 'ὲ', + 'Έ' => 'έ', + 'Ὴ' => 'ὴ', + 'Ή' => 'ή', + 'ῌ' => 'ῃ', + 'Ῐ' => 'ῐ', + 'Ῑ' => 'ῑ', + 'Ὶ' => 'ὶ', + 'Ί' => 'ί', + 'Ῠ' => 'ῠ', + 'Ῡ' => 'ῡ', + 'Ὺ' => 'ὺ', + 'Ύ' => 'ύ', + 'Ῥ' => 'ῥ', + 'Ὸ' => 'ὸ', + 'Ό' => 'ό', + 'Ὼ' => 'ὼ', + 'Ώ' => 'ώ', + 'ῼ' => 'ῳ', + 'Ω' => 'ω', + 'K' => 'k', + 'Å' => 'å', + 'Ⅎ' => 'ⅎ', + 'Ⅰ' => 'ⅰ', + 'Ⅱ' => 'ⅱ', + 'Ⅲ' => 'ⅲ', + 'Ⅳ' => 'ⅳ', + 'Ⅴ' => 'ⅴ', + 'Ⅵ' => 'ⅵ', + 'Ⅶ' => 'ⅶ', + 'Ⅷ' => 'ⅷ', + 'Ⅸ' => 'ⅸ', + 'Ⅹ' => 'ⅹ', + 'Ⅺ' => 'ⅺ', + 'Ⅻ' => 'ⅻ', + 'Ⅼ' => 'ⅼ', + 'Ⅽ' => 'ⅽ', + 'Ⅾ' => 'ⅾ', + 'Ⅿ' => 'ⅿ', + 'Ↄ' => 'ↄ', + 'Ⓐ' => 'ⓐ', + 'Ⓑ' => 'ⓑ', + 'Ⓒ' => 'ⓒ', + 'Ⓓ' => 'ⓓ', + 'Ⓔ' => 'ⓔ', + 'Ⓕ' => 'ⓕ', + 'Ⓖ' => 'ⓖ', + 'Ⓗ' => 'ⓗ', + 'Ⓘ' => 'ⓘ', + 'Ⓙ' => 'ⓙ', + 'Ⓚ' => 'ⓚ', + 'Ⓛ' => 'ⓛ', + 'Ⓜ' => 'ⓜ', + 'Ⓝ' => 'ⓝ', + 'Ⓞ' => 'ⓞ', + 'Ⓟ' => 'ⓟ', + 'Ⓠ' => 'ⓠ', + 'Ⓡ' => 'ⓡ', + 'Ⓢ' => 'ⓢ', + 'Ⓣ' => 'ⓣ', + 'Ⓤ' => 'ⓤ', + 'Ⓥ' => 'ⓥ', + 'Ⓦ' => 'ⓦ', + 'Ⓧ' => 'ⓧ', + 'Ⓨ' => 'ⓨ', + 'Ⓩ' => 'ⓩ', + 'Ⰰ' => 'ⰰ', + 'Ⰱ' => 'ⰱ', + 'Ⰲ' => 'ⰲ', + 'Ⰳ' => 'ⰳ', + 'Ⰴ' => 'ⰴ', + 'Ⰵ' => 'ⰵ', + 'Ⰶ' => 'ⰶ', + 'Ⰷ' => 'ⰷ', + 'Ⰸ' => 'ⰸ', + 'Ⰹ' => 'ⰹ', + 'Ⰺ' => 'ⰺ', + 'Ⰻ' => 'ⰻ', + 'Ⰼ' => 'ⰼ', + 'Ⰽ' => 'ⰽ', + 'Ⰾ' => 'ⰾ', + 'Ⰿ' => 'ⰿ', + 'Ⱀ' => 'ⱀ', + 'Ⱁ' => 'ⱁ', + 'Ⱂ' => 'ⱂ', + 'Ⱃ' => 'ⱃ', + 'Ⱄ' => 'ⱄ', + 'Ⱅ' => 'ⱅ', + 'Ⱆ' => 'ⱆ', + 'Ⱇ' => 'ⱇ', + 'Ⱈ' => 'ⱈ', + 'Ⱉ' => 'ⱉ', + 'Ⱊ' => 'ⱊ', + 'Ⱋ' => 'ⱋ', + 'Ⱌ' => 'ⱌ', + 'Ⱍ' => 'ⱍ', + 'Ⱎ' => 'ⱎ', + 'Ⱏ' => 'ⱏ', + 'Ⱐ' => 'ⱐ', + 'Ⱑ' => 'ⱑ', + 'Ⱒ' => 'ⱒ', + 'Ⱓ' => 'ⱓ', + 'Ⱔ' => 'ⱔ', + 'Ⱕ' => 'ⱕ', + 'Ⱖ' => 'ⱖ', + 'Ⱗ' => 'ⱗ', + 'Ⱘ' => 'ⱘ', + 'Ⱙ' => 'ⱙ', + 'Ⱚ' => 'ⱚ', + 'Ⱛ' => 'ⱛ', + 'Ⱜ' => 'ⱜ', + 'Ⱝ' => 'ⱝ', + 'Ⱞ' => 'ⱞ', + 'Ⱡ' => 'ⱡ', + 'Ɫ' => 'ɫ', + 'Ᵽ' => 'ᵽ', + 'Ɽ' => 'ɽ', + 'Ⱨ' => 'ⱨ', + 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', + 'Ɑ' => 'ɑ', + 'Ɱ' => 'ɱ', + 'Ɐ' => 'ɐ', + 'Ɒ' => 'ɒ', + 'Ⱳ' => 'ⱳ', + 'Ⱶ' => 'ⱶ', + 'Ȿ' => 'ȿ', + 'Ɀ' => 'ɀ', + 'Ⲁ' => 'ⲁ', + 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'ⲅ', + 'Ⲇ' => 'ⲇ', + 'Ⲉ' => 'ⲉ', + 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'ⲍ', + 'Ⲏ' => 'ⲏ', + 'Ⲑ' => 'ⲑ', + 'Ⲓ' => 'ⲓ', + 'Ⲕ' => 'ⲕ', + 'Ⲗ' => 'ⲗ', + 'Ⲙ' => 'ⲙ', + 'Ⲛ' => 'ⲛ', + 'Ⲝ' => 'ⲝ', + 'Ⲟ' => 'ⲟ', + 'Ⲡ' => 'ⲡ', + 'Ⲣ' => 'ⲣ', + 'Ⲥ' => 'ⲥ', + 'Ⲧ' => 'ⲧ', + 'Ⲩ' => 'ⲩ', + 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'ⲭ', + 'Ⲯ' => 'ⲯ', + 'Ⲱ' => 'ⲱ', + 'Ⲳ' => 'ⲳ', + 'Ⲵ' => 'ⲵ', + 'Ⲷ' => 'ⲷ', + 'Ⲹ' => 'ⲹ', + 'Ⲻ' => 'ⲻ', + 'Ⲽ' => 'ⲽ', + 'Ⲿ' => 'ⲿ', + 'Ⳁ' => 'ⳁ', + 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'ⳅ', + 'Ⳇ' => 'ⳇ', + 'Ⳉ' => 'ⳉ', + 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'ⳍ', + 'Ⳏ' => 'ⳏ', + 'Ⳑ' => 'ⳑ', + 'Ⳓ' => 'ⳓ', + 'Ⳕ' => 'ⳕ', + 'Ⳗ' => 'ⳗ', + 'Ⳙ' => 'ⳙ', + 'Ⳛ' => 'ⳛ', + 'Ⳝ' => 'ⳝ', + 'Ⳟ' => 'ⳟ', + 'Ⳡ' => 'ⳡ', + 'Ⳣ' => 'ⳣ', + 'Ⳬ' => 'ⳬ', + 'Ⳮ' => 'ⳮ', + 'Ⳳ' => 'ⳳ', + 'Ꙁ' => 'ꙁ', + 'Ꙃ' => 'ꙃ', + 'Ꙅ' => 'ꙅ', + 'Ꙇ' => 'ꙇ', + 'Ꙉ' => 'ꙉ', + 'Ꙋ' => 'ꙋ', + 'Ꙍ' => 'ꙍ', + 'Ꙏ' => 'ꙏ', + 'Ꙑ' => 'ꙑ', + 'Ꙓ' => 'ꙓ', + 'Ꙕ' => 'ꙕ', + 'Ꙗ' => 'ꙗ', + 'Ꙙ' => 'ꙙ', + 'Ꙛ' => 'ꙛ', + 'Ꙝ' => 'ꙝ', + 'Ꙟ' => 'ꙟ', + 'Ꙡ' => 'ꙡ', + 'Ꙣ' => 'ꙣ', + 'Ꙥ' => 'ꙥ', + 'Ꙧ' => 'ꙧ', + 'Ꙩ' => 'ꙩ', + 'Ꙫ' => 'ꙫ', + 'Ꙭ' => 'ꙭ', + 'Ꚁ' => 'ꚁ', + 'Ꚃ' => 'ꚃ', + 'Ꚅ' => 'ꚅ', + 'Ꚇ' => 'ꚇ', + 'Ꚉ' => 'ꚉ', + 'Ꚋ' => 'ꚋ', + 'Ꚍ' => 'ꚍ', + 'Ꚏ' => 'ꚏ', + 'Ꚑ' => 'ꚑ', + 'Ꚓ' => 'ꚓ', + 'Ꚕ' => 'ꚕ', + 'Ꚗ' => 'ꚗ', + 'Ꚙ' => 'ꚙ', + 'Ꚛ' => 'ꚛ', + 'Ꜣ' => 'ꜣ', + 'Ꜥ' => 'ꜥ', + 'Ꜧ' => 'ꜧ', + 'Ꜩ' => 'ꜩ', + 'Ꜫ' => 'ꜫ', + 'Ꜭ' => 'ꜭ', + 'Ꜯ' => 'ꜯ', + 'Ꜳ' => 'ꜳ', + 'Ꜵ' => 'ꜵ', + 'Ꜷ' => 'ꜷ', + 'Ꜹ' => 'ꜹ', + 'Ꜻ' => 'ꜻ', + 'Ꜽ' => 'ꜽ', + 'Ꜿ' => 'ꜿ', + 'Ꝁ' => 'ꝁ', + 'Ꝃ' => 'ꝃ', + 'Ꝅ' => 'ꝅ', + 'Ꝇ' => 'ꝇ', + 'Ꝉ' => 'ꝉ', + 'Ꝋ' => 'ꝋ', + 'Ꝍ' => 'ꝍ', + 'Ꝏ' => 'ꝏ', + 'Ꝑ' => 'ꝑ', + 'Ꝓ' => 'ꝓ', + 'Ꝕ' => 'ꝕ', + 'Ꝗ' => 'ꝗ', + 'Ꝙ' => 'ꝙ', + 'Ꝛ' => 'ꝛ', + 'Ꝝ' => 'ꝝ', + 'Ꝟ' => 'ꝟ', + 'Ꝡ' => 'ꝡ', + 'Ꝣ' => 'ꝣ', + 'Ꝥ' => 'ꝥ', + 'Ꝧ' => 'ꝧ', + 'Ꝩ' => 'ꝩ', + 'Ꝫ' => 'ꝫ', + 'Ꝭ' => 'ꝭ', + 'Ꝯ' => 'ꝯ', + 'Ꝺ' => 'ꝺ', + 'Ꝼ' => 'ꝼ', + 'Ᵹ' => 'ᵹ', + 'Ꝿ' => 'ꝿ', + 'Ꞁ' => 'ꞁ', + 'Ꞃ' => 'ꞃ', + 'Ꞅ' => 'ꞅ', + 'Ꞇ' => 'ꞇ', + 'Ꞌ' => 'ꞌ', + 'Ɥ' => 'ɥ', + 'Ꞑ' => 'ꞑ', + 'Ꞓ' => 'ꞓ', + 'Ꞗ' => 'ꞗ', + 'Ꞙ' => 'ꞙ', + 'Ꞛ' => 'ꞛ', + 'Ꞝ' => 'ꞝ', + 'Ꞟ' => 'ꞟ', + 'Ꞡ' => 'ꞡ', + 'Ꞣ' => 'ꞣ', + 'Ꞥ' => 'ꞥ', + 'Ꞧ' => 'ꞧ', + 'Ꞩ' => 'ꞩ', + 'Ɦ' => 'ɦ', + 'Ɜ' => 'ɜ', + 'Ɡ' => 'ɡ', + 'Ɬ' => 'ɬ', + 'Ɪ' => 'ɪ', + 'Ʞ' => 'ʞ', + 'Ʇ' => 'ʇ', + 'Ʝ' => 'ʝ', + 'Ꭓ' => 'ꭓ', + 'Ꞵ' => 'ꞵ', + 'Ꞷ' => 'ꞷ', + 'Ꞹ' => 'ꞹ', + 'Ꞻ' => 'ꞻ', + 'Ꞽ' => 'ꞽ', + 'Ꞿ' => 'ꞿ', + 'Ꟃ' => 'ꟃ', + 'Ꞔ' => 'ꞔ', + 'Ʂ' => 'ʂ', + 'Ᶎ' => 'ᶎ', + 'Ꟈ' => 'ꟈ', + 'Ꟊ' => 'ꟊ', + 'Ꟶ' => 'ꟶ', + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + '𐐀' => '𐐨', + '𐐁' => '𐐩', + '𐐂' => '𐐪', + '𐐃' => '𐐫', + '𐐄' => '𐐬', + '𐐅' => '𐐭', + '𐐆' => '𐐮', + '𐐇' => '𐐯', + '𐐈' => '𐐰', + '𐐉' => '𐐱', + '𐐊' => '𐐲', + '𐐋' => '𐐳', + '𐐌' => '𐐴', + '𐐍' => '𐐵', + '𐐎' => '𐐶', + '𐐏' => '𐐷', + '𐐐' => '𐐸', + '𐐑' => '𐐹', + '𐐒' => '𐐺', + '𐐓' => '𐐻', + '𐐔' => '𐐼', + '𐐕' => '𐐽', + '𐐖' => '𐐾', + '𐐗' => '𐐿', + '𐐘' => '𐑀', + '𐐙' => '𐑁', + '𐐚' => '𐑂', + '𐐛' => '𐑃', + '𐐜' => '𐑄', + '𐐝' => '𐑅', + '𐐞' => '𐑆', + '𐐟' => '𐑇', + '𐐠' => '𐑈', + '𐐡' => '𐑉', + '𐐢' => '𐑊', + '𐐣' => '𐑋', + '𐐤' => '𐑌', + '𐐥' => '𐑍', + '𐐦' => '𐑎', + '𐐧' => '𐑏', + '𐒰' => '𐓘', + '𐒱' => '𐓙', + '𐒲' => '𐓚', + '𐒳' => '𐓛', + '𐒴' => '𐓜', + '𐒵' => '𐓝', + '𐒶' => '𐓞', + '𐒷' => '𐓟', + '𐒸' => '𐓠', + '𐒹' => '𐓡', + '𐒺' => '𐓢', + '𐒻' => '𐓣', + '𐒼' => '𐓤', + '𐒽' => '𐓥', + '𐒾' => '𐓦', + '𐒿' => '𐓧', + '𐓀' => '𐓨', + '𐓁' => '𐓩', + '𐓂' => '𐓪', + '𐓃' => '𐓫', + '𐓄' => '𐓬', + '𐓅' => '𐓭', + '𐓆' => '𐓮', + '𐓇' => '𐓯', + '𐓈' => '𐓰', + '𐓉' => '𐓱', + '𐓊' => '𐓲', + '𐓋' => '𐓳', + '𐓌' => '𐓴', + '𐓍' => '𐓵', + '𐓎' => '𐓶', + '𐓏' => '𐓷', + '𐓐' => '𐓸', + '𐓑' => '𐓹', + '𐓒' => '𐓺', + '𐓓' => '𐓻', + '𐲀' => '𐳀', + '𐲁' => '𐳁', + '𐲂' => '𐳂', + '𐲃' => '𐳃', + '𐲄' => '𐳄', + '𐲅' => '𐳅', + '𐲆' => '𐳆', + '𐲇' => '𐳇', + '𐲈' => '𐳈', + '𐲉' => '𐳉', + '𐲊' => '𐳊', + '𐲋' => '𐳋', + '𐲌' => '𐳌', + '𐲍' => '𐳍', + '𐲎' => '𐳎', + '𐲏' => '𐳏', + '𐲐' => '𐳐', + '𐲑' => '𐳑', + '𐲒' => '𐳒', + '𐲓' => '𐳓', + '𐲔' => '𐳔', + '𐲕' => '𐳕', + '𐲖' => '𐳖', + '𐲗' => '𐳗', + '𐲘' => '𐳘', + '𐲙' => '𐳙', + '𐲚' => '𐳚', + '𐲛' => '𐳛', + '𐲜' => '𐳜', + '𐲝' => '𐳝', + '𐲞' => '𐳞', + '𐲟' => '𐳟', + '𐲠' => '𐳠', + '𐲡' => '𐳡', + '𐲢' => '𐳢', + '𐲣' => '𐳣', + '𐲤' => '𐳤', + '𐲥' => '𐳥', + '𐲦' => '𐳦', + '𐲧' => '𐳧', + '𐲨' => '𐳨', + '𐲩' => '𐳩', + '𐲪' => '𐳪', + '𐲫' => '𐳫', + '𐲬' => '𐳬', + '𐲭' => '𐳭', + '𐲮' => '𐳮', + '𐲯' => '𐳯', + '𐲰' => '𐳰', + '𐲱' => '𐳱', + '𐲲' => '𐳲', + '𑢠' => '𑣀', + '𑢡' => '𑣁', + '𑢢' => '𑣂', + '𑢣' => '𑣃', + '𑢤' => '𑣄', + '𑢥' => '𑣅', + '𑢦' => '𑣆', + '𑢧' => '𑣇', + '𑢨' => '𑣈', + '𑢩' => '𑣉', + '𑢪' => '𑣊', + '𑢫' => '𑣋', + '𑢬' => '𑣌', + '𑢭' => '𑣍', + '𑢮' => '𑣎', + '𑢯' => '𑣏', + '𑢰' => '𑣐', + '𑢱' => '𑣑', + '𑢲' => '𑣒', + '𑢳' => '𑣓', + '𑢴' => '𑣔', + '𑢵' => '𑣕', + '𑢶' => '𑣖', + '𑢷' => '𑣗', + '𑢸' => '𑣘', + '𑢹' => '𑣙', + '𑢺' => '𑣚', + '𑢻' => '𑣛', + '𑢼' => '𑣜', + '𑢽' => '𑣝', + '𑢾' => '𑣞', + '𑢿' => '𑣟', + '𖹀' => '𖹠', + '𖹁' => '𖹡', + '𖹂' => '𖹢', + '𖹃' => '𖹣', + '𖹄' => '𖹤', + '𖹅' => '𖹥', + '𖹆' => '𖹦', + '𖹇' => '𖹧', + '𖹈' => '𖹨', + '𖹉' => '𖹩', + '𖹊' => '𖹪', + '𖹋' => '𖹫', + '𖹌' => '𖹬', + '𖹍' => '𖹭', + '𖹎' => '𖹮', + '𖹏' => '𖹯', + '𖹐' => '𖹰', + '𖹑' => '𖹱', + '𖹒' => '𖹲', + '𖹓' => '𖹳', + '𖹔' => '𖹴', + '𖹕' => '𖹵', + '𖹖' => '𖹶', + '𖹗' => '𖹷', + '𖹘' => '𖹸', + '𖹙' => '𖹹', + '𖹚' => '𖹺', + '𖹛' => '𖹻', + '𖹜' => '𖹼', + '𖹝' => '𖹽', + '𖹞' => '𖹾', + '𖹟' => '𖹿', + '𞤀' => '𞤢', + '𞤁' => '𞤣', + '𞤂' => '𞤤', + '𞤃' => '𞤥', + '𞤄' => '𞤦', + '𞤅' => '𞤧', + '𞤆' => '𞤨', + '𞤇' => '𞤩', + '𞤈' => '𞤪', + '𞤉' => '𞤫', + '𞤊' => '𞤬', + '𞤋' => '𞤭', + '𞤌' => '𞤮', + '𞤍' => '𞤯', + '𞤎' => '𞤰', + '𞤏' => '𞤱', + '𞤐' => '𞤲', + '𞤑' => '𞤳', + '𞤒' => '𞤴', + '𞤓' => '𞤵', + '𞤔' => '𞤶', + '𞤕' => '𞤷', + '𞤖' => '𞤸', + '𞤗' => '𞤹', + '𞤘' => '𞤺', + '𞤙' => '𞤻', + '𞤚' => '𞤼', + '𞤛' => '𞤽', + '𞤜' => '𞤾', + '𞤝' => '𞤿', + '𞤞' => '𞥀', + '𞤟' => '𞥁', + '𞤠' => '𞥂', + '𞤡' => '𞥃', +); diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php new file mode 100644 index 00000000000..2a8f6e73b99 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php @@ -0,0 +1,5 @@ + 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + 'µ' => 'Μ', + 'à' => 'À', + 'á' => 'Á', + 'â' => 'Â', + 'ã' => 'Ã', + 'ä' => 'Ä', + 'å' => 'Å', + 'æ' => 'Æ', + 'ç' => 'Ç', + 'è' => 'È', + 'é' => 'É', + 'ê' => 'Ê', + 'ë' => 'Ë', + 'ì' => 'Ì', + 'í' => 'Í', + 'î' => 'Î', + 'ï' => 'Ï', + 'ð' => 'Ð', + 'ñ' => 'Ñ', + 'ò' => 'Ò', + 'ó' => 'Ó', + 'ô' => 'Ô', + 'õ' => 'Õ', + 'ö' => 'Ö', + 'ø' => 'Ø', + 'ù' => 'Ù', + 'ú' => 'Ú', + 'û' => 'Û', + 'ü' => 'Ü', + 'ý' => 'Ý', + 'þ' => 'Þ', + 'ÿ' => 'Ÿ', + 'ā' => 'Ā', + 'ă' => 'Ă', + 'ą' => 'Ą', + 'ć' => 'Ć', + 'ĉ' => 'Ĉ', + 'ċ' => 'Ċ', + 'č' => 'Č', + 'ď' => 'Ď', + 'đ' => 'Đ', + 'ē' => 'Ē', + 'ĕ' => 'Ĕ', + 'ė' => 'Ė', + 'ę' => 'Ę', + 'ě' => 'Ě', + 'ĝ' => 'Ĝ', + 'ğ' => 'Ğ', + 'ġ' => 'Ġ', + 'ģ' => 'Ģ', + 'ĥ' => 'Ĥ', + 'ħ' => 'Ħ', + 'ĩ' => 'Ĩ', + 'ī' => 'Ī', + 'ĭ' => 'Ĭ', + 'į' => 'Į', + 'ı' => 'I', + 'ij' => 'IJ', + 'ĵ' => 'Ĵ', + 'ķ' => 'Ķ', + 'ĺ' => 'Ĺ', + 'ļ' => 'Ļ', + 'ľ' => 'Ľ', + 'ŀ' => 'Ŀ', + 'ł' => 'Ł', + 'ń' => 'Ń', + 'ņ' => 'Ņ', + 'ň' => 'Ň', + 'ŋ' => 'Ŋ', + 'ō' => 'Ō', + 'ŏ' => 'Ŏ', + 'ő' => 'Ő', + 'œ' => 'Œ', + 'ŕ' => 'Ŕ', + 'ŗ' => 'Ŗ', + 'ř' => 'Ř', + 'ś' => 'Ś', + 'ŝ' => 'Ŝ', + 'ş' => 'Ş', + 'š' => 'Š', + 'ţ' => 'Ţ', + 'ť' => 'Ť', + 'ŧ' => 'Ŧ', + 'ũ' => 'Ũ', + 'ū' => 'Ū', + 'ŭ' => 'Ŭ', + 'ů' => 'Ů', + 'ű' => 'Ű', + 'ų' => 'Ų', + 'ŵ' => 'Ŵ', + 'ŷ' => 'Ŷ', + 'ź' => 'Ź', + 'ż' => 'Ż', + 'ž' => 'Ž', + 'ſ' => 'S', + 'ƀ' => 'Ƀ', + 'ƃ' => 'Ƃ', + 'ƅ' => 'Ƅ', + 'ƈ' => 'Ƈ', + 'ƌ' => 'Ƌ', + 'ƒ' => 'Ƒ', + 'ƕ' => 'Ƕ', + 'ƙ' => 'Ƙ', + 'ƚ' => 'Ƚ', + 'ƞ' => 'Ƞ', + 'ơ' => 'Ơ', + 'ƣ' => 'Ƣ', + 'ƥ' => 'Ƥ', + 'ƨ' => 'Ƨ', + 'ƭ' => 'Ƭ', + 'ư' => 'Ư', + 'ƴ' => 'Ƴ', + 'ƶ' => 'Ƶ', + 'ƹ' => 'Ƹ', + 'ƽ' => 'Ƽ', + 'ƿ' => 'Ƿ', + 'Dž' => 'DŽ', + 'dž' => 'DŽ', + 'Lj' => 'LJ', + 'lj' => 'LJ', + 'Nj' => 'NJ', + 'nj' => 'NJ', + 'ǎ' => 'Ǎ', + 'ǐ' => 'Ǐ', + 'ǒ' => 'Ǒ', + 'ǔ' => 'Ǔ', + 'ǖ' => 'Ǖ', + 'ǘ' => 'Ǘ', + 'ǚ' => 'Ǚ', + 'ǜ' => 'Ǜ', + 'ǝ' => 'Ǝ', + 'ǟ' => 'Ǟ', + 'ǡ' => 'Ǡ', + 'ǣ' => 'Ǣ', + 'ǥ' => 'Ǥ', + 'ǧ' => 'Ǧ', + 'ǩ' => 'Ǩ', + 'ǫ' => 'Ǫ', + 'ǭ' => 'Ǭ', + 'ǯ' => 'Ǯ', + 'Dz' => 'DZ', + 'dz' => 'DZ', + 'ǵ' => 'Ǵ', + 'ǹ' => 'Ǹ', + 'ǻ' => 'Ǻ', + 'ǽ' => 'Ǽ', + 'ǿ' => 'Ǿ', + 'ȁ' => 'Ȁ', + 'ȃ' => 'Ȃ', + 'ȅ' => 'Ȅ', + 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', + 'ȋ' => 'Ȋ', + 'ȍ' => 'Ȍ', + 'ȏ' => 'Ȏ', + 'ȑ' => 'Ȑ', + 'ȓ' => 'Ȓ', + 'ȕ' => 'Ȕ', + 'ȗ' => 'Ȗ', + 'ș' => 'Ș', + 'ț' => 'Ț', + 'ȝ' => 'Ȝ', + 'ȟ' => 'Ȟ', + 'ȣ' => 'Ȣ', + 'ȥ' => 'Ȥ', + 'ȧ' => 'Ȧ', + 'ȩ' => 'Ȩ', + 'ȫ' => 'Ȫ', + 'ȭ' => 'Ȭ', + 'ȯ' => 'Ȯ', + 'ȱ' => 'Ȱ', + 'ȳ' => 'Ȳ', + 'ȼ' => 'Ȼ', + 'ȿ' => 'Ȿ', + 'ɀ' => 'Ɀ', + 'ɂ' => 'Ɂ', + 'ɇ' => 'Ɇ', + 'ɉ' => 'Ɉ', + 'ɋ' => 'Ɋ', + 'ɍ' => 'Ɍ', + 'ɏ' => 'Ɏ', + 'ɐ' => 'Ɐ', + 'ɑ' => 'Ɑ', + 'ɒ' => 'Ɒ', + 'ɓ' => 'Ɓ', + 'ɔ' => 'Ɔ', + 'ɖ' => 'Ɖ', + 'ɗ' => 'Ɗ', + 'ə' => 'Ə', + 'ɛ' => 'Ɛ', + 'ɜ' => 'Ɜ', + 'ɠ' => 'Ɠ', + 'ɡ' => 'Ɡ', + 'ɣ' => 'Ɣ', + 'ɥ' => 'Ɥ', + 'ɦ' => 'Ɦ', + 'ɨ' => 'Ɨ', + 'ɩ' => 'Ɩ', + 'ɪ' => 'Ɪ', + 'ɫ' => 'Ɫ', + 'ɬ' => 'Ɬ', + 'ɯ' => 'Ɯ', + 'ɱ' => 'Ɱ', + 'ɲ' => 'Ɲ', + 'ɵ' => 'Ɵ', + 'ɽ' => 'Ɽ', + 'ʀ' => 'Ʀ', + 'ʂ' => 'Ʂ', + 'ʃ' => 'Ʃ', + 'ʇ' => 'Ʇ', + 'ʈ' => 'Ʈ', + 'ʉ' => 'Ʉ', + 'ʊ' => 'Ʊ', + 'ʋ' => 'Ʋ', + 'ʌ' => 'Ʌ', + 'ʒ' => 'Ʒ', + 'ʝ' => 'Ʝ', + 'ʞ' => 'Ʞ', + 'ͅ' => 'Ι', + 'ͱ' => 'Ͱ', + 'ͳ' => 'Ͳ', + 'ͷ' => 'Ͷ', + 'ͻ' => 'Ͻ', + 'ͼ' => 'Ͼ', + 'ͽ' => 'Ͽ', + 'ά' => 'Ά', + 'έ' => 'Έ', + 'ή' => 'Ή', + 'ί' => 'Ί', + 'α' => 'Α', + 'β' => 'Β', + 'γ' => 'Γ', + 'δ' => 'Δ', + 'ε' => 'Ε', + 'ζ' => 'Ζ', + 'η' => 'Η', + 'θ' => 'Θ', + 'ι' => 'Ι', + 'κ' => 'Κ', + 'λ' => 'Λ', + 'μ' => 'Μ', + 'ν' => 'Ν', + 'ξ' => 'Ξ', + 'ο' => 'Ο', + 'π' => 'Π', + 'ρ' => 'Ρ', + 'ς' => 'Σ', + 'σ' => 'Σ', + 'τ' => 'Τ', + 'υ' => 'Υ', + 'φ' => 'Φ', + 'χ' => 'Χ', + 'ψ' => 'Ψ', + 'ω' => 'Ω', + 'ϊ' => 'Ϊ', + 'ϋ' => 'Ϋ', + 'ό' => 'Ό', + 'ύ' => 'Ύ', + 'ώ' => 'Ώ', + 'ϐ' => 'Β', + 'ϑ' => 'Θ', + 'ϕ' => 'Φ', + 'ϖ' => 'Π', + 'ϗ' => 'Ϗ', + 'ϙ' => 'Ϙ', + 'ϛ' => 'Ϛ', + 'ϝ' => 'Ϝ', + 'ϟ' => 'Ϟ', + 'ϡ' => 'Ϡ', + 'ϣ' => 'Ϣ', + 'ϥ' => 'Ϥ', + 'ϧ' => 'Ϧ', + 'ϩ' => 'Ϩ', + 'ϫ' => 'Ϫ', + 'ϭ' => 'Ϭ', + 'ϯ' => 'Ϯ', + 'ϰ' => 'Κ', + 'ϱ' => 'Ρ', + 'ϲ' => 'Ϲ', + 'ϳ' => 'Ϳ', + 'ϵ' => 'Ε', + 'ϸ' => 'Ϸ', + 'ϻ' => 'Ϻ', + 'а' => 'А', + 'б' => 'Б', + 'в' => 'В', + 'г' => 'Г', + 'д' => 'Д', + 'е' => 'Е', + 'ж' => 'Ж', + 'з' => 'З', + 'и' => 'И', + 'й' => 'Й', + 'к' => 'К', + 'л' => 'Л', + 'м' => 'М', + 'н' => 'Н', + 'о' => 'О', + 'п' => 'П', + 'р' => 'Р', + 'с' => 'С', + 'т' => 'Т', + 'у' => 'У', + 'ф' => 'Ф', + 'х' => 'Х', + 'ц' => 'Ц', + 'ч' => 'Ч', + 'ш' => 'Ш', + 'щ' => 'Щ', + 'ъ' => 'Ъ', + 'ы' => 'Ы', + 'ь' => 'Ь', + 'э' => 'Э', + 'ю' => 'Ю', + 'я' => 'Я', + 'ѐ' => 'Ѐ', + 'ё' => 'Ё', + 'ђ' => 'Ђ', + 'ѓ' => 'Ѓ', + 'є' => 'Є', + 'ѕ' => 'Ѕ', + 'і' => 'І', + 'ї' => 'Ї', + 'ј' => 'Ј', + 'љ' => 'Љ', + 'њ' => 'Њ', + 'ћ' => 'Ћ', + 'ќ' => 'Ќ', + 'ѝ' => 'Ѝ', + 'ў' => 'Ў', + 'џ' => 'Џ', + 'ѡ' => 'Ѡ', + 'ѣ' => 'Ѣ', + 'ѥ' => 'Ѥ', + 'ѧ' => 'Ѧ', + 'ѩ' => 'Ѩ', + 'ѫ' => 'Ѫ', + 'ѭ' => 'Ѭ', + 'ѯ' => 'Ѯ', + 'ѱ' => 'Ѱ', + 'ѳ' => 'Ѳ', + 'ѵ' => 'Ѵ', + 'ѷ' => 'Ѷ', + 'ѹ' => 'Ѹ', + 'ѻ' => 'Ѻ', + 'ѽ' => 'Ѽ', + 'ѿ' => 'Ѿ', + 'ҁ' => 'Ҁ', + 'ҋ' => 'Ҋ', + 'ҍ' => 'Ҍ', + 'ҏ' => 'Ҏ', + 'ґ' => 'Ґ', + 'ғ' => 'Ғ', + 'ҕ' => 'Ҕ', + 'җ' => 'Җ', + 'ҙ' => 'Ҙ', + 'қ' => 'Қ', + 'ҝ' => 'Ҝ', + 'ҟ' => 'Ҟ', + 'ҡ' => 'Ҡ', + 'ң' => 'Ң', + 'ҥ' => 'Ҥ', + 'ҧ' => 'Ҧ', + 'ҩ' => 'Ҩ', + 'ҫ' => 'Ҫ', + 'ҭ' => 'Ҭ', + 'ү' => 'Ү', + 'ұ' => 'Ұ', + 'ҳ' => 'Ҳ', + 'ҵ' => 'Ҵ', + 'ҷ' => 'Ҷ', + 'ҹ' => 'Ҹ', + 'һ' => 'Һ', + 'ҽ' => 'Ҽ', + 'ҿ' => 'Ҿ', + 'ӂ' => 'Ӂ', + 'ӄ' => 'Ӄ', + 'ӆ' => 'Ӆ', + 'ӈ' => 'Ӈ', + 'ӊ' => 'Ӊ', + 'ӌ' => 'Ӌ', + 'ӎ' => 'Ӎ', + 'ӏ' => 'Ӏ', + 'ӑ' => 'Ӑ', + 'ӓ' => 'Ӓ', + 'ӕ' => 'Ӕ', + 'ӗ' => 'Ӗ', + 'ә' => 'Ә', + 'ӛ' => 'Ӛ', + 'ӝ' => 'Ӝ', + 'ӟ' => 'Ӟ', + 'ӡ' => 'Ӡ', + 'ӣ' => 'Ӣ', + 'ӥ' => 'Ӥ', + 'ӧ' => 'Ӧ', + 'ө' => 'Ө', + 'ӫ' => 'Ӫ', + 'ӭ' => 'Ӭ', + 'ӯ' => 'Ӯ', + 'ӱ' => 'Ӱ', + 'ӳ' => 'Ӳ', + 'ӵ' => 'Ӵ', + 'ӷ' => 'Ӷ', + 'ӹ' => 'Ӹ', + 'ӻ' => 'Ӻ', + 'ӽ' => 'Ӽ', + 'ӿ' => 'Ӿ', + 'ԁ' => 'Ԁ', + 'ԃ' => 'Ԃ', + 'ԅ' => 'Ԅ', + 'ԇ' => 'Ԇ', + 'ԉ' => 'Ԉ', + 'ԋ' => 'Ԋ', + 'ԍ' => 'Ԍ', + 'ԏ' => 'Ԏ', + 'ԑ' => 'Ԑ', + 'ԓ' => 'Ԓ', + 'ԕ' => 'Ԕ', + 'ԗ' => 'Ԗ', + 'ԙ' => 'Ԙ', + 'ԛ' => 'Ԛ', + 'ԝ' => 'Ԝ', + 'ԟ' => 'Ԟ', + 'ԡ' => 'Ԡ', + 'ԣ' => 'Ԣ', + 'ԥ' => 'Ԥ', + 'ԧ' => 'Ԧ', + 'ԩ' => 'Ԩ', + 'ԫ' => 'Ԫ', + 'ԭ' => 'Ԭ', + 'ԯ' => 'Ԯ', + 'ա' => 'Ա', + 'բ' => 'Բ', + 'գ' => 'Գ', + 'դ' => 'Դ', + 'ե' => 'Ե', + 'զ' => 'Զ', + 'է' => 'Է', + 'ը' => 'Ը', + 'թ' => 'Թ', + 'ժ' => 'Ժ', + 'ի' => 'Ի', + 'լ' => 'Լ', + 'խ' => 'Խ', + 'ծ' => 'Ծ', + 'կ' => 'Կ', + 'հ' => 'Հ', + 'ձ' => 'Ձ', + 'ղ' => 'Ղ', + 'ճ' => 'Ճ', + 'մ' => 'Մ', + 'յ' => 'Յ', + 'ն' => 'Ն', + 'շ' => 'Շ', + 'ո' => 'Ո', + 'չ' => 'Չ', + 'պ' => 'Պ', + 'ջ' => 'Ջ', + 'ռ' => 'Ռ', + 'ս' => 'Ս', + 'վ' => 'Վ', + 'տ' => 'Տ', + 'ր' => 'Ր', + 'ց' => 'Ց', + 'ւ' => 'Ւ', + 'փ' => 'Փ', + 'ք' => 'Ք', + 'օ' => 'Օ', + 'ֆ' => 'Ֆ', + 'ა' => 'Ა', + 'ბ' => 'Ბ', + 'გ' => 'Გ', + 'დ' => 'Დ', + 'ე' => 'Ე', + 'ვ' => 'Ვ', + 'ზ' => 'Ზ', + 'თ' => 'Თ', + 'ი' => 'Ი', + 'კ' => 'Კ', + 'ლ' => 'Ლ', + 'მ' => 'Მ', + 'ნ' => 'Ნ', + 'ო' => 'Ო', + 'პ' => 'Პ', + 'ჟ' => 'Ჟ', + 'რ' => 'Რ', + 'ს' => 'Ს', + 'ტ' => 'Ტ', + 'უ' => 'Უ', + 'ფ' => 'Ფ', + 'ქ' => 'Ქ', + 'ღ' => 'Ღ', + 'ყ' => 'Ყ', + 'შ' => 'Შ', + 'ჩ' => 'Ჩ', + 'ც' => 'Ც', + 'ძ' => 'Ძ', + 'წ' => 'Წ', + 'ჭ' => 'Ჭ', + 'ხ' => 'Ხ', + 'ჯ' => 'Ჯ', + 'ჰ' => 'Ჰ', + 'ჱ' => 'Ჱ', + 'ჲ' => 'Ჲ', + 'ჳ' => 'Ჳ', + 'ჴ' => 'Ჴ', + 'ჵ' => 'Ჵ', + 'ჶ' => 'Ჶ', + 'ჷ' => 'Ჷ', + 'ჸ' => 'Ჸ', + 'ჹ' => 'Ჹ', + 'ჺ' => 'Ჺ', + 'ჽ' => 'Ჽ', + 'ჾ' => 'Ჾ', + 'ჿ' => 'Ჿ', + 'ᏸ' => 'Ᏸ', + 'ᏹ' => 'Ᏹ', + 'ᏺ' => 'Ᏺ', + 'ᏻ' => 'Ᏻ', + 'ᏼ' => 'Ᏼ', + 'ᏽ' => 'Ᏽ', + 'ᲀ' => 'В', + 'ᲁ' => 'Д', + 'ᲂ' => 'О', + 'ᲃ' => 'С', + 'ᲄ' => 'Т', + 'ᲅ' => 'Т', + 'ᲆ' => 'Ъ', + 'ᲇ' => 'Ѣ', + 'ᲈ' => 'Ꙋ', + 'ᵹ' => 'Ᵹ', + 'ᵽ' => 'Ᵽ', + 'ᶎ' => 'Ᶎ', + 'ḁ' => 'Ḁ', + 'ḃ' => 'Ḃ', + 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', + 'ḉ' => 'Ḉ', + 'ḋ' => 'Ḋ', + 'ḍ' => 'Ḍ', + 'ḏ' => 'Ḏ', + 'ḑ' => 'Ḑ', + 'ḓ' => 'Ḓ', + 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', + 'ḙ' => 'Ḙ', + 'ḛ' => 'Ḛ', + 'ḝ' => 'Ḝ', + 'ḟ' => 'Ḟ', + 'ḡ' => 'Ḡ', + 'ḣ' => 'Ḣ', + 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', + 'ḩ' => 'Ḩ', + 'ḫ' => 'Ḫ', + 'ḭ' => 'Ḭ', + 'ḯ' => 'Ḯ', + 'ḱ' => 'Ḱ', + 'ḳ' => 'Ḳ', + 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', + 'ḹ' => 'Ḹ', + 'ḻ' => 'Ḻ', + 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', + 'ṁ' => 'Ṁ', + 'ṃ' => 'Ṃ', + 'ṅ' => 'Ṅ', + 'ṇ' => 'Ṇ', + 'ṉ' => 'Ṉ', + 'ṋ' => 'Ṋ', + 'ṍ' => 'Ṍ', + 'ṏ' => 'Ṏ', + 'ṑ' => 'Ṑ', + 'ṓ' => 'Ṓ', + 'ṕ' => 'Ṕ', + 'ṗ' => 'Ṗ', + 'ṙ' => 'Ṙ', + 'ṛ' => 'Ṛ', + 'ṝ' => 'Ṝ', + 'ṟ' => 'Ṟ', + 'ṡ' => 'Ṡ', + 'ṣ' => 'Ṣ', + 'ṥ' => 'Ṥ', + 'ṧ' => 'Ṧ', + 'ṩ' => 'Ṩ', + 'ṫ' => 'Ṫ', + 'ṭ' => 'Ṭ', + 'ṯ' => 'Ṯ', + 'ṱ' => 'Ṱ', + 'ṳ' => 'Ṳ', + 'ṵ' => 'Ṵ', + 'ṷ' => 'Ṷ', + 'ṹ' => 'Ṹ', + 'ṻ' => 'Ṻ', + 'ṽ' => 'Ṽ', + 'ṿ' => 'Ṿ', + 'ẁ' => 'Ẁ', + 'ẃ' => 'Ẃ', + 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', + 'ẉ' => 'Ẉ', + 'ẋ' => 'Ẋ', + 'ẍ' => 'Ẍ', + 'ẏ' => 'Ẏ', + 'ẑ' => 'Ẑ', + 'ẓ' => 'Ẓ', + 'ẕ' => 'Ẕ', + 'ẛ' => 'Ṡ', + 'ạ' => 'Ạ', + 'ả' => 'Ả', + 'ấ' => 'Ấ', + 'ầ' => 'Ầ', + 'ẩ' => 'Ẩ', + 'ẫ' => 'Ẫ', + 'ậ' => 'Ậ', + 'ắ' => 'Ắ', + 'ằ' => 'Ằ', + 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', + 'ặ' => 'Ặ', + 'ẹ' => 'Ẹ', + 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', + 'ế' => 'Ế', + 'ề' => 'Ề', + 'ể' => 'Ể', + 'ễ' => 'Ễ', + 'ệ' => 'Ệ', + 'ỉ' => 'Ỉ', + 'ị' => 'Ị', + 'ọ' => 'Ọ', + 'ỏ' => 'Ỏ', + 'ố' => 'Ố', + 'ồ' => 'Ồ', + 'ổ' => 'Ổ', + 'ỗ' => 'Ỗ', + 'ộ' => 'Ộ', + 'ớ' => 'Ớ', + 'ờ' => 'Ờ', + 'ở' => 'Ở', + 'ỡ' => 'Ỡ', + 'ợ' => 'Ợ', + 'ụ' => 'Ụ', + 'ủ' => 'Ủ', + 'ứ' => 'Ứ', + 'ừ' => 'Ừ', + 'ử' => 'Ử', + 'ữ' => 'Ữ', + 'ự' => 'Ự', + 'ỳ' => 'Ỳ', + 'ỵ' => 'Ỵ', + 'ỷ' => 'Ỷ', + 'ỹ' => 'Ỹ', + 'ỻ' => 'Ỻ', + 'ỽ' => 'Ỽ', + 'ỿ' => 'Ỿ', + 'ἀ' => 'Ἀ', + 'ἁ' => 'Ἁ', + 'ἂ' => 'Ἂ', + 'ἃ' => 'Ἃ', + 'ἄ' => 'Ἄ', + 'ἅ' => 'Ἅ', + 'ἆ' => 'Ἆ', + 'ἇ' => 'Ἇ', + 'ἐ' => 'Ἐ', + 'ἑ' => 'Ἑ', + 'ἒ' => 'Ἒ', + 'ἓ' => 'Ἓ', + 'ἔ' => 'Ἔ', + 'ἕ' => 'Ἕ', + 'ἠ' => 'Ἠ', + 'ἡ' => 'Ἡ', + 'ἢ' => 'Ἢ', + 'ἣ' => 'Ἣ', + 'ἤ' => 'Ἤ', + 'ἥ' => 'Ἥ', + 'ἦ' => 'Ἦ', + 'ἧ' => 'Ἧ', + 'ἰ' => 'Ἰ', + 'ἱ' => 'Ἱ', + 'ἲ' => 'Ἲ', + 'ἳ' => 'Ἳ', + 'ἴ' => 'Ἴ', + 'ἵ' => 'Ἵ', + 'ἶ' => 'Ἶ', + 'ἷ' => 'Ἷ', + 'ὀ' => 'Ὀ', + 'ὁ' => 'Ὁ', + 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', + 'ὄ' => 'Ὄ', + 'ὅ' => 'Ὅ', + 'ὑ' => 'Ὑ', + 'ὓ' => 'Ὓ', + 'ὕ' => 'Ὕ', + 'ὗ' => 'Ὗ', + 'ὠ' => 'Ὠ', + 'ὡ' => 'Ὡ', + 'ὢ' => 'Ὢ', + 'ὣ' => 'Ὣ', + 'ὤ' => 'Ὤ', + 'ὥ' => 'Ὥ', + 'ὦ' => 'Ὦ', + 'ὧ' => 'Ὧ', + 'ὰ' => 'Ὰ', + 'ά' => 'Ά', + 'ὲ' => 'Ὲ', + 'έ' => 'Έ', + 'ὴ' => 'Ὴ', + 'ή' => 'Ή', + 'ὶ' => 'Ὶ', + 'ί' => 'Ί', + 'ὸ' => 'Ὸ', + 'ό' => 'Ό', + 'ὺ' => 'Ὺ', + 'ύ' => 'Ύ', + 'ὼ' => 'Ὼ', + 'ώ' => 'Ώ', + 'ᾀ' => 'ᾈ', + 'ᾁ' => 'ᾉ', + 'ᾂ' => 'ᾊ', + 'ᾃ' => 'ᾋ', + 'ᾄ' => 'ᾌ', + 'ᾅ' => 'ᾍ', + 'ᾆ' => 'ᾎ', + 'ᾇ' => 'ᾏ', + 'ᾐ' => 'ᾘ', + 'ᾑ' => 'ᾙ', + 'ᾒ' => 'ᾚ', + 'ᾓ' => 'ᾛ', + 'ᾔ' => 'ᾜ', + 'ᾕ' => 'ᾝ', + 'ᾖ' => 'ᾞ', + 'ᾗ' => 'ᾟ', + 'ᾠ' => 'ᾨ', + 'ᾡ' => 'ᾩ', + 'ᾢ' => 'ᾪ', + 'ᾣ' => 'ᾫ', + 'ᾤ' => 'ᾬ', + 'ᾥ' => 'ᾭ', + 'ᾦ' => 'ᾮ', + 'ᾧ' => 'ᾯ', + 'ᾰ' => 'Ᾰ', + 'ᾱ' => 'Ᾱ', + 'ᾳ' => 'ᾼ', + 'ι' => 'Ι', + 'ῃ' => 'ῌ', + 'ῐ' => 'Ῐ', + 'ῑ' => 'Ῑ', + 'ῠ' => 'Ῠ', + 'ῡ' => 'Ῡ', + 'ῥ' => 'Ῥ', + 'ῳ' => 'ῼ', + 'ⅎ' => 'Ⅎ', + 'ⅰ' => 'Ⅰ', + 'ⅱ' => 'Ⅱ', + 'ⅲ' => 'Ⅲ', + 'ⅳ' => 'Ⅳ', + 'ⅴ' => 'Ⅴ', + 'ⅵ' => 'Ⅵ', + 'ⅶ' => 'Ⅶ', + 'ⅷ' => 'Ⅷ', + 'ⅸ' => 'Ⅸ', + 'ⅹ' => 'Ⅹ', + 'ⅺ' => 'Ⅺ', + 'ⅻ' => 'Ⅻ', + 'ⅼ' => 'Ⅼ', + 'ⅽ' => 'Ⅽ', + 'ⅾ' => 'Ⅾ', + 'ⅿ' => 'Ⅿ', + 'ↄ' => 'Ↄ', + 'ⓐ' => 'Ⓐ', + 'ⓑ' => 'Ⓑ', + 'ⓒ' => 'Ⓒ', + 'ⓓ' => 'Ⓓ', + 'ⓔ' => 'Ⓔ', + 'ⓕ' => 'Ⓕ', + 'ⓖ' => 'Ⓖ', + 'ⓗ' => 'Ⓗ', + 'ⓘ' => 'Ⓘ', + 'ⓙ' => 'Ⓙ', + 'ⓚ' => 'Ⓚ', + 'ⓛ' => 'Ⓛ', + 'ⓜ' => 'Ⓜ', + 'ⓝ' => 'Ⓝ', + 'ⓞ' => 'Ⓞ', + 'ⓟ' => 'Ⓟ', + 'ⓠ' => 'Ⓠ', + 'ⓡ' => 'Ⓡ', + 'ⓢ' => 'Ⓢ', + 'ⓣ' => 'Ⓣ', + 'ⓤ' => 'Ⓤ', + 'ⓥ' => 'Ⓥ', + 'ⓦ' => 'Ⓦ', + 'ⓧ' => 'Ⓧ', + 'ⓨ' => 'Ⓨ', + 'ⓩ' => 'Ⓩ', + 'ⰰ' => 'Ⰰ', + 'ⰱ' => 'Ⰱ', + 'ⰲ' => 'Ⰲ', + 'ⰳ' => 'Ⰳ', + 'ⰴ' => 'Ⰴ', + 'ⰵ' => 'Ⰵ', + 'ⰶ' => 'Ⰶ', + 'ⰷ' => 'Ⰷ', + 'ⰸ' => 'Ⰸ', + 'ⰹ' => 'Ⰹ', + 'ⰺ' => 'Ⰺ', + 'ⰻ' => 'Ⰻ', + 'ⰼ' => 'Ⰼ', + 'ⰽ' => 'Ⰽ', + 'ⰾ' => 'Ⰾ', + 'ⰿ' => 'Ⰿ', + 'ⱀ' => 'Ⱀ', + 'ⱁ' => 'Ⱁ', + 'ⱂ' => 'Ⱂ', + 'ⱃ' => 'Ⱃ', + 'ⱄ' => 'Ⱄ', + 'ⱅ' => 'Ⱅ', + 'ⱆ' => 'Ⱆ', + 'ⱇ' => 'Ⱇ', + 'ⱈ' => 'Ⱈ', + 'ⱉ' => 'Ⱉ', + 'ⱊ' => 'Ⱊ', + 'ⱋ' => 'Ⱋ', + 'ⱌ' => 'Ⱌ', + 'ⱍ' => 'Ⱍ', + 'ⱎ' => 'Ⱎ', + 'ⱏ' => 'Ⱏ', + 'ⱐ' => 'Ⱐ', + 'ⱑ' => 'Ⱑ', + 'ⱒ' => 'Ⱒ', + 'ⱓ' => 'Ⱓ', + 'ⱔ' => 'Ⱔ', + 'ⱕ' => 'Ⱕ', + 'ⱖ' => 'Ⱖ', + 'ⱗ' => 'Ⱗ', + 'ⱘ' => 'Ⱘ', + 'ⱙ' => 'Ⱙ', + 'ⱚ' => 'Ⱚ', + 'ⱛ' => 'Ⱛ', + 'ⱜ' => 'Ⱜ', + 'ⱝ' => 'Ⱝ', + 'ⱞ' => 'Ⱞ', + 'ⱡ' => 'Ⱡ', + 'ⱥ' => 'Ⱥ', + 'ⱦ' => 'Ⱦ', + 'ⱨ' => 'Ⱨ', + 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', + 'ⱳ' => 'Ⱳ', + 'ⱶ' => 'Ⱶ', + 'ⲁ' => 'Ⲁ', + 'ⲃ' => 'Ⲃ', + 'ⲅ' => 'Ⲅ', + 'ⲇ' => 'Ⲇ', + 'ⲉ' => 'Ⲉ', + 'ⲋ' => 'Ⲋ', + 'ⲍ' => 'Ⲍ', + 'ⲏ' => 'Ⲏ', + 'ⲑ' => 'Ⲑ', + 'ⲓ' => 'Ⲓ', + 'ⲕ' => 'Ⲕ', + 'ⲗ' => 'Ⲗ', + 'ⲙ' => 'Ⲙ', + 'ⲛ' => 'Ⲛ', + 'ⲝ' => 'Ⲝ', + 'ⲟ' => 'Ⲟ', + 'ⲡ' => 'Ⲡ', + 'ⲣ' => 'Ⲣ', + 'ⲥ' => 'Ⲥ', + 'ⲧ' => 'Ⲧ', + 'ⲩ' => 'Ⲩ', + 'ⲫ' => 'Ⲫ', + 'ⲭ' => 'Ⲭ', + 'ⲯ' => 'Ⲯ', + 'ⲱ' => 'Ⲱ', + 'ⲳ' => 'Ⲳ', + 'ⲵ' => 'Ⲵ', + 'ⲷ' => 'Ⲷ', + 'ⲹ' => 'Ⲹ', + 'ⲻ' => 'Ⲻ', + 'ⲽ' => 'Ⲽ', + 'ⲿ' => 'Ⲿ', + 'ⳁ' => 'Ⳁ', + 'ⳃ' => 'Ⳃ', + 'ⳅ' => 'Ⳅ', + 'ⳇ' => 'Ⳇ', + 'ⳉ' => 'Ⳉ', + 'ⳋ' => 'Ⳋ', + 'ⳍ' => 'Ⳍ', + 'ⳏ' => 'Ⳏ', + 'ⳑ' => 'Ⳑ', + 'ⳓ' => 'Ⳓ', + 'ⳕ' => 'Ⳕ', + 'ⳗ' => 'Ⳗ', + 'ⳙ' => 'Ⳙ', + 'ⳛ' => 'Ⳛ', + 'ⳝ' => 'Ⳝ', + 'ⳟ' => 'Ⳟ', + 'ⳡ' => 'Ⳡ', + 'ⳣ' => 'Ⳣ', + 'ⳬ' => 'Ⳬ', + 'ⳮ' => 'Ⳮ', + 'ⳳ' => 'Ⳳ', + 'ⴀ' => 'Ⴀ', + 'ⴁ' => 'Ⴁ', + 'ⴂ' => 'Ⴂ', + 'ⴃ' => 'Ⴃ', + 'ⴄ' => 'Ⴄ', + 'ⴅ' => 'Ⴅ', + 'ⴆ' => 'Ⴆ', + 'ⴇ' => 'Ⴇ', + 'ⴈ' => 'Ⴈ', + 'ⴉ' => 'Ⴉ', + 'ⴊ' => 'Ⴊ', + 'ⴋ' => 'Ⴋ', + 'ⴌ' => 'Ⴌ', + 'ⴍ' => 'Ⴍ', + 'ⴎ' => 'Ⴎ', + 'ⴏ' => 'Ⴏ', + 'ⴐ' => 'Ⴐ', + 'ⴑ' => 'Ⴑ', + 'ⴒ' => 'Ⴒ', + 'ⴓ' => 'Ⴓ', + 'ⴔ' => 'Ⴔ', + 'ⴕ' => 'Ⴕ', + 'ⴖ' => 'Ⴖ', + 'ⴗ' => 'Ⴗ', + 'ⴘ' => 'Ⴘ', + 'ⴙ' => 'Ⴙ', + 'ⴚ' => 'Ⴚ', + 'ⴛ' => 'Ⴛ', + 'ⴜ' => 'Ⴜ', + 'ⴝ' => 'Ⴝ', + 'ⴞ' => 'Ⴞ', + 'ⴟ' => 'Ⴟ', + 'ⴠ' => 'Ⴠ', + 'ⴡ' => 'Ⴡ', + 'ⴢ' => 'Ⴢ', + 'ⴣ' => 'Ⴣ', + 'ⴤ' => 'Ⴤ', + 'ⴥ' => 'Ⴥ', + 'ⴧ' => 'Ⴧ', + 'ⴭ' => 'Ⴭ', + 'ꙁ' => 'Ꙁ', + 'ꙃ' => 'Ꙃ', + 'ꙅ' => 'Ꙅ', + 'ꙇ' => 'Ꙇ', + 'ꙉ' => 'Ꙉ', + 'ꙋ' => 'Ꙋ', + 'ꙍ' => 'Ꙍ', + 'ꙏ' => 'Ꙏ', + 'ꙑ' => 'Ꙑ', + 'ꙓ' => 'Ꙓ', + 'ꙕ' => 'Ꙕ', + 'ꙗ' => 'Ꙗ', + 'ꙙ' => 'Ꙙ', + 'ꙛ' => 'Ꙛ', + 'ꙝ' => 'Ꙝ', + 'ꙟ' => 'Ꙟ', + 'ꙡ' => 'Ꙡ', + 'ꙣ' => 'Ꙣ', + 'ꙥ' => 'Ꙥ', + 'ꙧ' => 'Ꙧ', + 'ꙩ' => 'Ꙩ', + 'ꙫ' => 'Ꙫ', + 'ꙭ' => 'Ꙭ', + 'ꚁ' => 'Ꚁ', + 'ꚃ' => 'Ꚃ', + 'ꚅ' => 'Ꚅ', + 'ꚇ' => 'Ꚇ', + 'ꚉ' => 'Ꚉ', + 'ꚋ' => 'Ꚋ', + 'ꚍ' => 'Ꚍ', + 'ꚏ' => 'Ꚏ', + 'ꚑ' => 'Ꚑ', + 'ꚓ' => 'Ꚓ', + 'ꚕ' => 'Ꚕ', + 'ꚗ' => 'Ꚗ', + 'ꚙ' => 'Ꚙ', + 'ꚛ' => 'Ꚛ', + 'ꜣ' => 'Ꜣ', + 'ꜥ' => 'Ꜥ', + 'ꜧ' => 'Ꜧ', + 'ꜩ' => 'Ꜩ', + 'ꜫ' => 'Ꜫ', + 'ꜭ' => 'Ꜭ', + 'ꜯ' => 'Ꜯ', + 'ꜳ' => 'Ꜳ', + 'ꜵ' => 'Ꜵ', + 'ꜷ' => 'Ꜷ', + 'ꜹ' => 'Ꜹ', + 'ꜻ' => 'Ꜻ', + 'ꜽ' => 'Ꜽ', + 'ꜿ' => 'Ꜿ', + 'ꝁ' => 'Ꝁ', + 'ꝃ' => 'Ꝃ', + 'ꝅ' => 'Ꝅ', + 'ꝇ' => 'Ꝇ', + 'ꝉ' => 'Ꝉ', + 'ꝋ' => 'Ꝋ', + 'ꝍ' => 'Ꝍ', + 'ꝏ' => 'Ꝏ', + 'ꝑ' => 'Ꝑ', + 'ꝓ' => 'Ꝓ', + 'ꝕ' => 'Ꝕ', + 'ꝗ' => 'Ꝗ', + 'ꝙ' => 'Ꝙ', + 'ꝛ' => 'Ꝛ', + 'ꝝ' => 'Ꝝ', + 'ꝟ' => 'Ꝟ', + 'ꝡ' => 'Ꝡ', + 'ꝣ' => 'Ꝣ', + 'ꝥ' => 'Ꝥ', + 'ꝧ' => 'Ꝧ', + 'ꝩ' => 'Ꝩ', + 'ꝫ' => 'Ꝫ', + 'ꝭ' => 'Ꝭ', + 'ꝯ' => 'Ꝯ', + 'ꝺ' => 'Ꝺ', + 'ꝼ' => 'Ꝼ', + 'ꝿ' => 'Ꝿ', + 'ꞁ' => 'Ꞁ', + 'ꞃ' => 'Ꞃ', + 'ꞅ' => 'Ꞅ', + 'ꞇ' => 'Ꞇ', + 'ꞌ' => 'Ꞌ', + 'ꞑ' => 'Ꞑ', + 'ꞓ' => 'Ꞓ', + 'ꞔ' => 'Ꞔ', + 'ꞗ' => 'Ꞗ', + 'ꞙ' => 'Ꞙ', + 'ꞛ' => 'Ꞛ', + 'ꞝ' => 'Ꞝ', + 'ꞟ' => 'Ꞟ', + 'ꞡ' => 'Ꞡ', + 'ꞣ' => 'Ꞣ', + 'ꞥ' => 'Ꞥ', + 'ꞧ' => 'Ꞧ', + 'ꞩ' => 'Ꞩ', + 'ꞵ' => 'Ꞵ', + 'ꞷ' => 'Ꞷ', + 'ꞹ' => 'Ꞹ', + 'ꞻ' => 'Ꞻ', + 'ꞽ' => 'Ꞽ', + 'ꞿ' => 'Ꞿ', + 'ꟃ' => 'Ꟃ', + 'ꟈ' => 'Ꟈ', + 'ꟊ' => 'Ꟊ', + 'ꟶ' => 'Ꟶ', + 'ꭓ' => 'Ꭓ', + 'ꭰ' => 'Ꭰ', + 'ꭱ' => 'Ꭱ', + 'ꭲ' => 'Ꭲ', + 'ꭳ' => 'Ꭳ', + 'ꭴ' => 'Ꭴ', + 'ꭵ' => 'Ꭵ', + 'ꭶ' => 'Ꭶ', + 'ꭷ' => 'Ꭷ', + 'ꭸ' => 'Ꭸ', + 'ꭹ' => 'Ꭹ', + 'ꭺ' => 'Ꭺ', + 'ꭻ' => 'Ꭻ', + 'ꭼ' => 'Ꭼ', + 'ꭽ' => 'Ꭽ', + 'ꭾ' => 'Ꭾ', + 'ꭿ' => 'Ꭿ', + 'ꮀ' => 'Ꮀ', + 'ꮁ' => 'Ꮁ', + 'ꮂ' => 'Ꮂ', + 'ꮃ' => 'Ꮃ', + 'ꮄ' => 'Ꮄ', + 'ꮅ' => 'Ꮅ', + 'ꮆ' => 'Ꮆ', + 'ꮇ' => 'Ꮇ', + 'ꮈ' => 'Ꮈ', + 'ꮉ' => 'Ꮉ', + 'ꮊ' => 'Ꮊ', + 'ꮋ' => 'Ꮋ', + 'ꮌ' => 'Ꮌ', + 'ꮍ' => 'Ꮍ', + 'ꮎ' => 'Ꮎ', + 'ꮏ' => 'Ꮏ', + 'ꮐ' => 'Ꮐ', + 'ꮑ' => 'Ꮑ', + 'ꮒ' => 'Ꮒ', + 'ꮓ' => 'Ꮓ', + 'ꮔ' => 'Ꮔ', + 'ꮕ' => 'Ꮕ', + 'ꮖ' => 'Ꮖ', + 'ꮗ' => 'Ꮗ', + 'ꮘ' => 'Ꮘ', + 'ꮙ' => 'Ꮙ', + 'ꮚ' => 'Ꮚ', + 'ꮛ' => 'Ꮛ', + 'ꮜ' => 'Ꮜ', + 'ꮝ' => 'Ꮝ', + 'ꮞ' => 'Ꮞ', + 'ꮟ' => 'Ꮟ', + 'ꮠ' => 'Ꮠ', + 'ꮡ' => 'Ꮡ', + 'ꮢ' => 'Ꮢ', + 'ꮣ' => 'Ꮣ', + 'ꮤ' => 'Ꮤ', + 'ꮥ' => 'Ꮥ', + 'ꮦ' => 'Ꮦ', + 'ꮧ' => 'Ꮧ', + 'ꮨ' => 'Ꮨ', + 'ꮩ' => 'Ꮩ', + 'ꮪ' => 'Ꮪ', + 'ꮫ' => 'Ꮫ', + 'ꮬ' => 'Ꮬ', + 'ꮭ' => 'Ꮭ', + 'ꮮ' => 'Ꮮ', + 'ꮯ' => 'Ꮯ', + 'ꮰ' => 'Ꮰ', + 'ꮱ' => 'Ꮱ', + 'ꮲ' => 'Ꮲ', + 'ꮳ' => 'Ꮳ', + 'ꮴ' => 'Ꮴ', + 'ꮵ' => 'Ꮵ', + 'ꮶ' => 'Ꮶ', + 'ꮷ' => 'Ꮷ', + 'ꮸ' => 'Ꮸ', + 'ꮹ' => 'Ꮹ', + 'ꮺ' => 'Ꮺ', + 'ꮻ' => 'Ꮻ', + 'ꮼ' => 'Ꮼ', + 'ꮽ' => 'Ꮽ', + 'ꮾ' => 'Ꮾ', + 'ꮿ' => 'Ꮿ', + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𐓘' => '𐒰', + '𐓙' => '𐒱', + '𐓚' => '𐒲', + '𐓛' => '𐒳', + '𐓜' => '𐒴', + '𐓝' => '𐒵', + '𐓞' => '𐒶', + '𐓟' => '𐒷', + '𐓠' => '𐒸', + '𐓡' => '𐒹', + '𐓢' => '𐒺', + '𐓣' => '𐒻', + '𐓤' => '𐒼', + '𐓥' => '𐒽', + '𐓦' => '𐒾', + '𐓧' => '𐒿', + '𐓨' => '𐓀', + '𐓩' => '𐓁', + '𐓪' => '𐓂', + '𐓫' => '𐓃', + '𐓬' => '𐓄', + '𐓭' => '𐓅', + '𐓮' => '𐓆', + '𐓯' => '𐓇', + '𐓰' => '𐓈', + '𐓱' => '𐓉', + '𐓲' => '𐓊', + '𐓳' => '𐓋', + '𐓴' => '𐓌', + '𐓵' => '𐓍', + '𐓶' => '𐓎', + '𐓷' => '𐓏', + '𐓸' => '𐓐', + '𐓹' => '𐓑', + '𐓺' => '𐓒', + '𐓻' => '𐓓', + '𐳀' => '𐲀', + '𐳁' => '𐲁', + '𐳂' => '𐲂', + '𐳃' => '𐲃', + '𐳄' => '𐲄', + '𐳅' => '𐲅', + '𐳆' => '𐲆', + '𐳇' => '𐲇', + '𐳈' => '𐲈', + '𐳉' => '𐲉', + '𐳊' => '𐲊', + '𐳋' => '𐲋', + '𐳌' => '𐲌', + '𐳍' => '𐲍', + '𐳎' => '𐲎', + '𐳏' => '𐲏', + '𐳐' => '𐲐', + '𐳑' => '𐲑', + '𐳒' => '𐲒', + '𐳓' => '𐲓', + '𐳔' => '𐲔', + '𐳕' => '𐲕', + '𐳖' => '𐲖', + '𐳗' => '𐲗', + '𐳘' => '𐲘', + '𐳙' => '𐲙', + '𐳚' => '𐲚', + '𐳛' => '𐲛', + '𐳜' => '𐲜', + '𐳝' => '𐲝', + '𐳞' => '𐲞', + '𐳟' => '𐲟', + '𐳠' => '𐲠', + '𐳡' => '𐲡', + '𐳢' => '𐲢', + '𐳣' => '𐲣', + '𐳤' => '𐲤', + '𐳥' => '𐲥', + '𐳦' => '𐲦', + '𐳧' => '𐲧', + '𐳨' => '𐲨', + '𐳩' => '𐲩', + '𐳪' => '𐲪', + '𐳫' => '𐲫', + '𐳬' => '𐲬', + '𐳭' => '𐲭', + '𐳮' => '𐲮', + '𐳯' => '𐲯', + '𐳰' => '𐲰', + '𐳱' => '𐲱', + '𐳲' => '𐲲', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', + '𖹠' => '𖹀', + '𖹡' => '𖹁', + '𖹢' => '𖹂', + '𖹣' => '𖹃', + '𖹤' => '𖹄', + '𖹥' => '𖹅', + '𖹦' => '𖹆', + '𖹧' => '𖹇', + '𖹨' => '𖹈', + '𖹩' => '𖹉', + '𖹪' => '𖹊', + '𖹫' => '𖹋', + '𖹬' => '𖹌', + '𖹭' => '𖹍', + '𖹮' => '𖹎', + '𖹯' => '𖹏', + '𖹰' => '𖹐', + '𖹱' => '𖹑', + '𖹲' => '𖹒', + '𖹳' => '𖹓', + '𖹴' => '𖹔', + '𖹵' => '𖹕', + '𖹶' => '𖹖', + '𖹷' => '𖹗', + '𖹸' => '𖹘', + '𖹹' => '𖹙', + '𖹺' => '𖹚', + '𖹻' => '𖹛', + '𖹼' => '𖹜', + '𖹽' => '𖹝', + '𖹾' => '𖹞', + '𖹿' => '𖹟', + '𞤢' => '𞤀', + '𞤣' => '𞤁', + '𞤤' => '𞤂', + '𞤥' => '𞤃', + '𞤦' => '𞤄', + '𞤧' => '𞤅', + '𞤨' => '𞤆', + '𞤩' => '𞤇', + '𞤪' => '𞤈', + '𞤫' => '𞤉', + '𞤬' => '𞤊', + '𞤭' => '𞤋', + '𞤮' => '𞤌', + '𞤯' => '𞤍', + '𞤰' => '𞤎', + '𞤱' => '𞤏', + '𞤲' => '𞤐', + '𞤳' => '𞤑', + '𞤴' => '𞤒', + '𞤵' => '𞤓', + '𞤶' => '𞤔', + '𞤷' => '𞤕', + '𞤸' => '𞤖', + '𞤹' => '𞤗', + '𞤺' => '𞤘', + '𞤻' => '𞤙', + '𞤼' => '𞤚', + '𞤽' => '𞤛', + '𞤾' => '𞤜', + '𞤿' => '𞤝', + '𞥀' => '𞤞', + '𞥁' => '𞤟', + '𞥂' => '𞤠', + '𞥃' => '𞤡', +); diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap.php b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 00000000000..c45624c9f79 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language($language = null) { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str($string, &$result = []) { parse_str($string, $result); } +} +if (!function_exists('mb_strlen')) { + function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap80.php b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap80.php new file mode 100644 index 00000000000..e86754e52e1 --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding(array|string $string, string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader(string $string): string { return p\Mbstring::mb_decode_mimeheader($string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader(string $string, string $charset = null, string $transfer_encoding = null, string $newline = "\r\n", int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity(string $string, array $map, string $encoding = null): string { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity(string $string, array $map, string $encoding = null, bool $hex = false): string { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case(string $string, int $mode, string $encoding = null): string { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding(string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language(string $language = null): string|bool { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases(string $encoding): array { return p\Mbstring::mb_encoding_aliases($encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding(array|string|null $value = null, string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding(string $string, array|string|null $encodings = null, bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str(string $string, &$result = array()): bool { parse_str($string, $result); } +} +if (!function_exists('mb_strlen')) { + function mb_strlen(string $string, string $encoding = null): int { return p\Mbstring::mb_strlen($string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos(string $haystack, string $needle, int $offset = 0, string $encoding = null): int|false { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower(string $string, string $encoding = null): string { return p\Mbstring::mb_strtolower($string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper(string $string, string $encoding = null): string { return p\Mbstring::mb_strtoupper($string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr(string $string, int $start, int $length = null, string $encoding = null): string { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos(string $haystack, string $needle, int $offset = 0, string $encoding = null): int|false { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr(string $haystack, string $needle, bool $before_needle = false, string $encoding = null): string|false { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr(string $haystack, string $needle, bool $before_needle = false, string $encoding = null): string|false { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr(string $haystack, string $needle, bool $before_needle = false, string $encoding = null): string|false { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos(string $haystack, string $needle, int $offset = 0, string $encoding = null): int|false { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos(string $haystack, string $needle, int $offset = 0, string $encoding = null): int|false { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr(string $haystack, string $needle, bool $before_needle = false, string $encoding = null): string|false { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info(string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info($type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output(string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth(string $string, string $encoding = null): int { return p\Mbstring::mb_strwidth($string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count(string $haystack, string $needle, string $encoding = null): int { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler(string $string, int $status): string { return p\Mbstring::mb_output_handler($string, $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input(string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables(string $to_encoding, array|string $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, $var, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord(string $string, string $encoding = null): int|false { return p\Mbstring::mb_ord($string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr(int $codepoint, string $encoding = null): string|false { return p\Mbstring::mb_chr($codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub(string $string, string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding($string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split(string $string, int $length = 1, string $encoding = null): array { return p\Mbstring::mb_str_split($string, $length, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/composer.json b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 00000000000..ca8263859bd --- /dev/null +++ b/lib/phpspreadsheet/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +}