diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f3d033a7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,21 @@ +# build config +/.scrutinizer.yml export-ignore +/.travis.yml export-ignore +/php_cs.dist export-ignore +/phpmd.xml.dist export-ignore +/phpstan.neon export-ignore + +/composer.lock export-ignore + +# git files +/.gitignore export-ignore +/.gitattributes export-ignore + +# project directories +/build export-ignore +/docs export-ignore +/samples export-ignore + +# tests +/phpunit.xml.dist export-ignore +/tests export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index e5deb643..b2ec7e23 100644 --- a/.gitignore +++ b/.gitignore @@ -6,14 +6,19 @@ Thumbs.db Desktop.ini .idea _build +/build phpunit.xml composer.lock composer.phar vendor /report +/build /samples/resources /samples/results /.settings phpword.ini /.buildpath -/.project \ No newline at end of file +/.scannerwork +/.project +/nbproject +/.php_cs.cache diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 00000000..895ed80f --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,146 @@ +notName('pclzip.lib.php') + ->notName('OLERead.php') + ->in('samples') + ->in('src') + ->in('tests'); + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setFinder($finder) + ->setRules(array( + 'array_syntax' => array('syntax' => 'long'), + 'binary_operator_spaces' => array('align_double_arrow' => true), + 'blank_line_after_namespace' => true, + 'blank_line_after_opening_tag' => false, + 'blank_line_before_return' => true, + 'braces' => true, + 'cast_spaces' => true, + 'class_definition' => true, + 'class_keyword_remove' => false, // ::class keyword gives us beter support in IDE + 'combine_consecutive_unsets' => true, + 'concat_space' => array('spacing' => 'one'), + 'declare_equal_normalize' => true, + 'declare_strict_types' => false, // Too early to adopt strict types + 'dir_constant' => true, + 'elseif' => true, + 'encoding' => true, + 'ereg_to_preg' => true, + 'full_opening_tag' => true, + 'function_declaration' => true, + 'function_typehint_space' => true, + 'general_phpdoc_annotation_remove' => false, // No use for that + 'hash_to_slash_comment' => true, + 'header_comment' => false, // We don't use common header in all our files + 'heredoc_to_nowdoc' => false, // Not sure about this one + 'is_null' => false, // Risky + 'include' => true, + 'indentation_type' => true, + 'line_ending' => true, + 'linebreak_after_opening_tag' => true, + 'lowercase_cast' => true, + 'lowercase_constants' => true, + 'lowercase_keywords' => true, + 'mb_str_functions' => false, // No, too dangerous to change that + 'method_argument_space' => true, + 'method_separation' => true, + 'modernize_types_casting' => true, + 'native_function_casing' => true, + 'native_function_invocation'=> false, // This is risky and seems to be micro-optimization that make code uglier so not worth it, at least for now + 'new_with_braces' => true, + 'no_alias_functions' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_blank_lines_before_namespace' => false, // we want 1 blank line before namespace + 'no_closing_tag' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_consecutive_blank_lines' => array('break', 'continue', 'extra', 'return', 'throw', 'use', 'useTrait', 'curly_brace_block', 'parenthesis_brace_block', 'square_brace_block'), + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => true, + 'no_multiline_whitespace_around_double_arrow' => true, + 'no_multiline_whitespace_before_semicolons' => true, + 'no_php4_constructor' => true, + 'no_short_bool_cast' => true, + 'no_short_echo_tag' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_comma_in_list_call' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unreachable_default_argument_value' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'normalize_index_brace' => true, + 'not_operator_with_space' => false, // No we prefer to keep '!' without spaces + 'not_operator_with_successor_space' => false, // idem + 'object_operator_without_whitespace' => true, + 'ordered_class_elements' => false, // We prefer to keep some freedom + 'ordered_imports' => true, + 'php_unit_construct' => true, + 'php_unit_dedicate_assert' => true, + 'php_unit_fqcn_annotation' => true, + 'php_unit_strict' => false, // We sometime actually need assertEquals + 'phpdoc_add_missing_param_annotation' => true, + 'phpdoc_align' => false, // Waste of time + 'phpdoc_annotation_without_dot' => true, + 'phpdoc_indent' => true, + 'phpdoc_inline_tag' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_alias_tag' => true, + 'phpdoc_no_empty_return' => true, + 'phpdoc_no_package' => true, + 'phpdoc_no_useless_inheritdoc' => true, + 'phpdoc_order' => true, + 'phpdoc_return_self_reference' => true, + 'phpdoc_scalar' => true, + 'phpdoc_separation' => false, + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_summary' => false, + 'phpdoc_to_comment' => true, + 'phpdoc_trim' => true, + 'phpdoc_types' => true, + 'phpdoc_var_without_name' => true, + 'pow_to_exponentiation' => false, + 'pre_increment' => false, + 'protected_to_private' => true, + 'psr0' => true, + 'psr4' => true, + 'random_api_migration' => false, // This breaks our unit tests + 'return_type_declaration' => true, + 'self_accessor' => true, + 'semicolon_after_instruction' => false, // Buggy in `samples/index.php` + 'short_scalar_cast' => true, + 'silenced_deprecation_error' => true, + 'simplified_null_return' => false, // While technically correct we prefer to be explicit when returning null + 'single_blank_line_at_eof' => true, + 'single_blank_line_before_namespace' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'single_quote' => true, + 'space_after_semicolon' => true, + 'standardize_not_equals' => true, + 'strict_comparison' => false, // No, too dangerous to change that + 'strict_param' => false, // No, too dangerous to change that + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_null_coalescing' => false, // Cannot use that with PHP 5.6 + 'trailing_comma_in_multiline_array' => true, + 'trim_array_spaces' => false, + 'unary_operator_spaces' => true, + 'visibility_required' => true, + 'whitespace_after_comma_in_array' => true, + )); diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 6f982d8e..2b395afd 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,3 +1,8 @@ +build: + nodes: + analysis: + tests: + override: [php-scrutinizer-run] filter: excluded_paths: [ 'vendor/*', 'tests/*', 'samples/*', 'src/PhpWord/Shared/PCLZip/*' ] @@ -14,8 +19,8 @@ tools: config: ruleset: phpmd.xml.dist external_code_coverage: - enabled: true - timeout: 900 + enabled: false + timeout: 1200 php_cpd: true # php_sim: # Temporarily disabled to allow focus on things other than duplicates # min_mass: 40 diff --git a/.travis.yml b/.travis.yml index 0712f734..881decfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,36 @@ language: php +dist: precise + php: - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - - hhvm + - 7.1 + - 7.2 + - 7.3 matrix: - allow_failures: + include: - php: 7.0 - - php: hhvm + env: COVERAGE=1 + - php: 5.3 + env: COMPOSER_MEMORY_LIMIT=2G + - php: 7.3 + env: DEPENDENCIES="--ignore-platform-reqs" + exclude: + - php: 5.3 + - php: 7.0 + - php: 7.3 + allow_failures: + - php: 7.3 + +cache: + directories: + - $HOME/.composer/cache + - .php-cs.cache env: global: @@ -23,28 +42,29 @@ before_install: - sudo apt-get install -y graphviz before_script: + ## Deactivate xdebug if we don't do code coverage + - if [ -z "$COVERAGE" ]; then phpenv config-rm xdebug.ini || echo "xdebug not available" ; fi ## Composer - composer self-update - - composer install --prefer-source + - travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi) ## PHPDocumentor - - mkdir -p build/docs + ##- mkdir -p build/docs - mkdir -p build/coverage script: ## PHP_CodeSniffer - - ./vendor/bin/phpcs src/ tests/ --standard=PSR2 -n --ignore=src/PhpWord/Shared/PCLZip + - if [ -z "$COVERAGE" ]; then ./vendor/bin/phpcs src/ tests/ --standard=PSR2 -n --ignore=src/PhpWord/Shared/PCLZip ; fi + ## PHP-CS-Fixer + - if [ -n "$COVERAGE" ]; then ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run ; fi ## PHP Mess Detector - - ./vendor/bin/phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php + - if [ -z "$COVERAGE" ]; then ./vendor/bin/phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php ; fi ## PHPUnit - - ./vendor/bin/phpunit -c ./ --coverage-text --coverage-html ./build/coverage + - ./vendor/bin/phpunit -c ./ $(if [ -n "$COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) ## PHPLOC - - ./vendor/bin/phploc src/ + - if [ -z "$COVERAGE" ]; then ./vendor/bin/phploc src/ ; fi ## PHPDocumentor - - ./vendor/bin/phpdoc -q -d ./src -t ./build/docs --ignore "*/src/PhpWord/Shared/*/*" --template="responsive-twig" + ##- if [ -z "$COVERAGE" ]; then ./vendor/bin/phpdoc -q -d ./src -t ./build/docs --ignore "*/src/PhpWord/Shared/*/*" --template="responsive-twig" ; fi -after_script: - ## PHPDocumentor - - bash .travis_shell_after_success.sh - ## Scrutinizer - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml +after_success: + ## Coveralls + - if [ -n "$COVERAGE" ]; then travis_retry php vendor/bin/php-coveralls -v ; fi diff --git a/.travis_shell_after_success.sh b/.travis_shell_after_success.sh deleted file mode 100644 index 35c7a338..00000000 --- a/.travis_shell_after_success.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -echo "--DEBUG--" -echo "TRAVIS_REPO_SLUG: $TRAVIS_REPO_SLUG" -echo "TRAVIS_PHP_VERSION: $TRAVIS_PHP_VERSION" -echo "TRAVIS_PULL_REQUEST: $TRAVIS_PULL_REQUEST" - -if [ "$TRAVIS_REPO_SLUG" == "PHPOffice/PHPWord" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_PHP_VERSION" == "5.5" ]; then - - echo -e "Publishing PHPDoc...\n" - - cp -R build/docs $HOME/docs-latest - cp -R build/coverage $HOME/coverage-latest - - cd $HOME - git config --global user.email "travis@travis-ci.org" - git config --global user.name "travis-ci" - git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/PHPOffice/PHPWord gh-pages > /dev/null - - cd gh-pages - echo "--DEBUG : Suppression" - git rm -rf ./docs/$TRAVIS_BRANCH - - echo "--DEBUG : Dossier" - mkdir -p docs/$TRAVIS_BRANCH - mkdir -p coverage/$TRAVIS_BRANCH - - echo "--DEBUG : Copie" - cp -Rf $HOME/docs-latest/* ./docs/$TRAVIS_BRANCH/ - cp -Rf $HOME/coverage-latest/* ./coverage/$TRAVIS_BRANCH/ - - echo "--DEBUG : Git" - git add -f . - git commit -m "PHPDocumentor (Travis Build: $TRAVIS_BUILD_NUMBER - Branch: $TRAVIS_BRANCH)" - git push -fq origin gh-pages > /dev/null - - echo -e "Published PHPDoc to gh-pages.\n" - -fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f38dda..79ae2511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,115 @@ Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +v0.16.0 (xx xxx 2018) +---------------------- +### Added +- Add setting Chart Title and Legend visibility @Tom-Magill #1433 + +### Fixed +- Fix regex in `cloneBlock` function @nicoder #1269 +- HTML Title Writer loses text when Title contains a TextRun instead a string. @begnini #1436 +- Adding table layout to the generated HTML @aarangara #1441 +- Fix loading of Sharepoint document @Garrcomm #1498 +- RTF writer: Round getPageSizeW and getPageSizeH to avoid decimals @Patrick64 #1493 +- Fix parsing of Office 365 documents @Timanx #1485 + +v0.15.0 (14 Jul 2018) +---------------------- +### Added +- Parsing of `align` HTML attribute - @troosan #1231 +- Parse formatting inside HTML lists - @troosan @samimussbach #1239 #945 #1215 #508 +- Parsing of CSS `direction` instruction, HTML `lang` attribute, formatting inside table cell - @troosan #1273 #1252 #1254 +- Add support for Track changes @Cip @troosan #354 #1262 +- Add support for fixed Table Layout @aoloe @ekopach @troosan #841 #1276 +- Add support for Cell Spacing @dox07 @troosan #1040 +- Add parsing of formatting inside lists @atomicalnet @troosan #594 +- Added support for Vertically Raised or Lowered Text (w:position) @anrikun @troosan #640 +- Add support for MACROBUTTON field @phryneas @troosan #1021 +- Add support for Hyphenation @Trainmaster #1282 (Document: `autoHyphenation`, `consecutiveHyphenLimit`, `hyphenationZone`, `doNotHyphenateCaps`, Paragraph: `suppressAutoHyphens`) +- Added support for Floating Table Positioning (tblpPr) @anrikun #639 +- Added support for Image text wrapping distance @troosan #1310 +- Added parsing of CSS line-height and text-indent in HTML reader @troosan #1316 +- Added the ability to enable gridlines and axislabels on charts @FrankMeyer #576 +- Add support for table indent (tblInd) @Trainmaster #1343 +- Added parsing of internal links in HTML reader @lalop #1336 +- Several improvements to charts @JAEK-S #1332 +- Add parsing of html image in base64 format @jgpATs2w #1382 +- Added Support for Indentation & Tabs on RTF Writer. @smaug1985 #1405 +- Allows decimal numbers in HTML line-height style @jgpATs2w #1413 + +### Fixed +- Fix reading of docx default style - @troosan #1238 +- Fix the size unit of when parsing html images - @troosan #1254 +- Fixed HTML parsing of nested lists - @troosan #1265 +- Save PNG alpha information when using remote images. @samsullivan #779 +- Fix parsing of `` tag. @troosan #1274 +- Bookmark are not writton as internal link in html writer @troosan #1263 +- It should be possible to add a Footnote in a ListItemRun @troosan #1287 #1287 +- Fix colspan and rowspan for tables in HTML Writer @mattbolt #1292 +- Fix parsing of Heading and Title formating @troosan @gthomas2 #465 +- Fix Dateformat typo, fix hours casing, add Month-Day-Year formats @ComputerTinker #591 +- Support reading of w:drawing for documents produced by word 2011+ @gthomas2 #464 #1324 +- Fix missing column width in ODText writer @potofcoffee #413 +- Disable entity loader before parsing XML to avoid XXE injection @Tom4t0 #1427 + +### Changed +- Remove zend-stdlib dependency @Trainmaster #1284 +- The default unit for `\PhpOffice\PhpWord\Style\Image` changed from `px` to `pt`. + +### Miscelaneous +- Drop GitHub pages, switch to coveralls for code coverage analysis @czosel #1360 + +v0.14.0 (29 Dec 2017) +---------------------- +This release fixes several bugs and adds some new features. +This version brings compatibility with PHP 7.0 & 7.1 + +### Added +- Possibility to control the footnote numbering - @troosan #1068 +- Image creation from string - @troosan #937 +- Introduced the `\PhpOffice\PhpWord\SimpleType\NumberFormat` simple type. - @troosan +- Support for ContextualSpacing - @postHawk #1088 +- Possiblity to hide spelling and/or grammatical errors - @troosan #542 +- Possiblity to set default document language as well as changing the language for each text element - @troosan #1108 +- Support for Comments - @troosan #1067 +- Support for paragraph textAlignment - @troosan #1165 +- Add support for HTML underline tag in addHtml - @zNightFalLz #1186 +- Add support for HTML
in addHtml - @anrikun @troosan #659 +- Allow to change cell width unit - guillaume-ro-fr #986 +- Allow to change the line height rule @troosan +- Implement PageBreak for odt writer @cookiekiller #863 #824 +- Allow to force an update of all fields on opening a document - @troosan #951 +- Allow adding a CheckBox in a TextRun - @irond #727 +- Add support for HTML img tag - @srggroup #934 +- Add support for password protection for docx - @mariahaubner #1019 + +### Fixed +- Loosen dependency to Zend +- Images are not being printed when generating PDF - @hubertinio #1074 #431 +- Fixed some PHP 7 warnings - @ likeuntomurphy #927 +- Fixed PHP 7.2 compatibility (renamed `Object` class names to `ObjectElement`) - @SailorMax #1185 +- Fixed Word 97 reader - @alsofronie @Benpxpx @mario-rivera #912 #920 #892 +- Fixed image loading over https - @troosan #988 +- Impossibility to set different even and odd page headers - @troosan #981 +- Fixed Word2007 reader where unnecessary paragraphs were being created - @donghaobo #1043 #620 +- Fixed Word2007 reader where margins were not being read correctly - @slowprog #885 #1008 +- Impossible to add element PreserveText in Section - @rvanlaak #452 +- Added missing options for numbering format - @troosan #1041 +- Fixed impossibility to set a different footer for first page - @ctrlaltca #1116, @aoloe #875 +- Fixed styles not being applied by HTML writer, better pdf output - @sarke #1047 #500 #1139 +- Fixed read docx error when document contains image from remote url - @FBnil #1173 #1176 +- Padded the $args array to remove error - @kaigoh #1150, @reformed #870 +- Fix incorrect image size between windows and mac - @bskrtich #874 +- Fix adding HTML table to document - @mogilvie @arivanbastos #324 +- Fix parsing on/off values (w:val="true|false|1|0|on|off") - @troosan #1221 #1219 +- Fix error on Empty Dropdown Entry - @ComputerTinker #592 + +### Deprecated +- PhpWord->getProtection(), get it from the settings instead PhpWord->getSettings()->getDocumentProtection(); + + + v0.13.0 (31 July 2016) ------------------- This release brings several improvements in `TemplateProcessor`, automatic output escaping feature for OOXML, ODF, HTML, and RTF (turned off, by default). @@ -22,6 +131,7 @@ Manual installation feature has been dropped since the release. Please, use [Com - Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371 - Renamed the `align` option of `NumberingLevel`, `Frame`, `Table`, and `Paragraph` styles into `alignment`. - @RomanSyroeshko - Improved performance of `TemplateProcessor::setValue()`. - @kazitanvirahsan #614, #617 +- Fixed some HTML tags not rendering any output (p, header & table) - #257, #324 - @twmobius and @garethellis ### Deprecated - `getAlign` and `setAlign` methods of `NumberingLevel`, `Frame`, `Table`, and `Paragraph` styles. @@ -399,4 +509,4 @@ This is the first release after a long development hiatus in [CodePlex](https:// - Basic CI with Travis - @Progi1984 - Added PHPWord_Exception and exception when could not copy the template - @Progi1984 - IMPROVED: Moved examples out of Classes directory - @Progi1984 -- IMPROVED: Advanced string replace in setValue for Template - @Esmeraldo [#49](http://phpword.codeplex.com/workitem/49) \ No newline at end of file +- IMPROVED: Advanced string replace in setValue for Template - @Esmeraldo [#49](http://phpword.codeplex.com/workitem/49) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 335ad2d5..e62f2e6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ We want to create a high quality document writer and reader library that people - **Be brief, but be bold**. State your issues briefly. But speak out your ideas loudly, even if you can't or don't know how to implement it right away. The world will be better with limitless innovations. - **Follow PHP-FIG standards**. We follow PHP Standards Recommendations (PSRs) by [PHP Framework Interoperability Group](http://www.php-fig.org/). If you're not familiar with these standards, please, [familiarize yourself now](https://github.com/php-fig/fig-standards). Also, please, use [PHPCodeSniffer](http://pear.php.net/package/PHP_CodeSniffer/) to validate your code against PSRs. -- **Test your code**. Nobody else knows your code better than you. So, it's completely yours mission to test the changes you made before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and recommend you using this tool too. [Here](https://phpunit.de/presentations.html) you can find PHPUnit best practices and additional information on effective unit testing, which helps us making PHPWord better day to day. Do not hesitate to smoke it carefully. It's a great investment in quality of your work, and it saves you years of life. +- **Test your code**. Nobody else knows your code better than you. So, it's completely your mission to test the changes you made before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and recommend you using this tool too. [Here](https://phpunit.de/presentations.html) you can find PHPUnit best practices and additional information on effective unit testing, which helps us making PHPWord better day to day. Do not hesitate to smoke it carefully. It's a great investment in quality of your work, and it saves you years of life. - **Request pull in separate branch**. Do not submit your request to the master branch. But create a separate branch named specifically for the issue that you addressed. Read [GitHub manual](https://help.github.com/articles/using-pull-requests) to find out more about this. If you are new to GitHub, read [this short manual](https://help.github.com/articles/fork-a-repo) to get yourself familiar with forks and how git works in general. [This video](http://www.youtube.com/watch?v=-zvHQXnBO6c) explains how to synchronize your Github Fork with the Branch of PHPWord. That's it. Thank you for your interest in PHPWord, and welcome! diff --git a/README.md b/README.md index 18f4c55f..7531a6bc 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # ![PHPWord](https://rawgit.com/PHPOffice/PHPWord/develop/docs/images/phpword.svg "PHPWord") -## :mag_right: PHPWord is looking for a new maintainer :crown: :pencil: ([#948](https://github.com/PHPOffice/PHPWord/issues/948)) - [![Latest Stable Version](https://poser.pugx.org/phpoffice/phpword/v/stable.png)](https://packagist.org/packages/phpoffice/phpword) [![Build Status](https://travis-ci.org/PHPOffice/PHPWord.svg?branch=master)](https://travis-ci.org/PHPOffice/PHPWord) [![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/quality-score.png?s=b5997ce59ac2816b4514f3a38de9900f6d492c1d)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/) -[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/coverage.png?s=742a98745725c562955440edc8d2c39d7ff5ae25)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/) +[![Coverage Status](https://coveralls.io/repos/github/PHPOffice/PHPWord/badge.svg?branch=develop)](https://coveralls.io/github/PHPOffice/PHPWord?branch=develop) [![Total Downloads](https://poser.pugx.org/phpoffice/phpword/downloads.png)](https://packagist.org/packages/phpoffice/phpword) [![License](https://poser.pugx.org/phpoffice/phpword/license.png)](https://packagist.org/packages/phpoffice/phpword) [![Join the chat at https://gitter.im/PHPOffice/PHPWord](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPWord) PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF. -PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/develop/). +PHPWord is an open source project licensed under the terms of [LGPL version 3](https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/). + +If you have any questions, please ask on [StackOverFlow](https://stackoverflow.com/questions/tagged/phpword) Read more about PHPWord: @@ -22,7 +22,6 @@ Read more about PHPWord: - [Getting started](#getting-started) - [Contributing](#contributing) - [Developers' Documentation](http://phpword.readthedocs.org/) -- [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/) ## Features @@ -57,8 +56,7 @@ PHPWord requires the following: - PHP 5.3.3+ - [XML Parser extension](http://www.php.net/manual/en/xml.installation.php) - [Zend\Escaper component](http://framework.zend.com/manual/current/en/modules/zend.escaper.introduction.html) -- Zend\Stdlib component -- [Zend\Validator component](http://framework.zend.com/manual/current/en/modules/zend.validator.html) +- [Zend\Stdlib component](http://framework.zend.com/manual/current/en/modules/zend.stdlib.hydrator.html) - [Zip extension](http://php.net/manual/en/book.zip.php) (optional, used to write OOXML and ODF) - [GD extension](http://php.net/manual/en/book.image.php) (optional, used to add images) - [XMLWriter extension](http://php.net/manual/en/book.xmlwriter.php) (optional, used to write OOXML and ODF) @@ -68,14 +66,22 @@ PHPWord requires the following: ## Installation PHPWord is installed via [Composer](https://getcomposer.org/). -You just need to [add dependency](https://getcomposer.org/doc/04-schema.md#package-links>) on PHPWord into your package. +To [add a dependency](https://getcomposer.org/doc/04-schema.md#package-links>) to PHPWord in your project, either -Example: +Run the following to use the latest stable version +```sh + composer require phpoffice/phpword +``` +or if you want the latest master version +```sh + composer require phpoffice/phpword:dev-master +``` +You can of course also manually edit your composer.json file ```json { "require": { - "phpoffice/phpword": "v0.13.*" + "phpoffice/phpword": "v0.14.*" } } ``` @@ -154,7 +160,8 @@ $objWriter->save('helloWorld.html'); /* Note: we skip PDF, because "HTML-to-PDF" approach is used to create PDF documents. */ ``` -More examples are provided in the [samples folder](samples/). You can also read the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/) for more detail. +More examples are provided in the [samples folder](samples/). For an easy access to those samples launch `php -S localhost:8000` in the samples directory then browse to [http://localhost:8000](http://localhost:8000) to view the samples. +You can also read the [Developers' Documentation](http://phpword.readthedocs.org/) and the [API Documentation](http://phpoffice.github.io/PHPWord/docs/master/) for more detail. ## Contributing diff --git a/VERSION b/VERSION deleted file mode 100644 index 51de3305..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.13.0 \ No newline at end of file diff --git a/bootstrap.php b/bootstrap.php index 11939fee..740e3d04 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. test bootstrap * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/composer.json b/composer.json index c49eb9cd..5d8a855b 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ }, { "name": "Franck Lefevre", - "homepage": "http://blog.rootslabs.net" + "homepage": "https://rootslabs.net/blog/" }, { "name": "Ivan Lanin", @@ -29,25 +29,52 @@ { "name": "Roman Syroeshko", "homepage": "http://ru.linkedin.com/pub/roman-syroeshko/34/a53/994/" + }, + { + "name": "Antoine de Troostembergh" } ], + "scripts": { + "test": [ + "phpunit --color=always" + ], + "test-no-coverage": [ + "phpunit --color=always --no-coverage" + ], + "check": [ + "php-cs-fixer fix --ansi --dry-run --diff", + "phpcs --report-width=200 --report-summary --report-full samples/ src/ tests/ --ignore=src/PhpWord/Shared/PCLZip --standard=PSR2 -n", + "phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php", + "@test" + ], + "fix": [ + "php-cs-fixer fix --ansi" + ] + }, + "scripts-descriptions": { + "test": "Runs all unit tests", + "test-no-coverage": "Runs all unit tests, without code coverage", + "check": "Runs PHP CheckStyle and PHP Mess detector", + "fix": "Fixes issues found by PHP-CS" + }, "require": { - "php": ">=5.3.3", + "php": "^5.3.3 || ^7.0", "ext-xml": "*", - "zendframework/zend-escaper": "2.4.*", - "zendframework/zend-stdlib": "2.4.*", - "zendframework/zend-validator": "2.4.*", - "phpoffice/common": "0.2.*" + "zendframework/zend-escaper": "^2.2", + "phpoffice/common": "^0.2.9" }, "require-dev": { - "phpunit/phpunit": "3.7.*", - "phpdocumentor/phpdocumentor":"2.*", - "squizlabs/php_codesniffer": "1.*", + "ext-zip": "*", + "ext-gd": "*", + "phpunit/phpunit": "^4.8.36 || ^7.0", + "squizlabs/php_codesniffer": "^2.9", + "friendsofphp/php-cs-fixer": "^2.2", "phpmd/phpmd": "2.*", - "phploc/phploc": "2.*", - "dompdf/dompdf":"0.6.*", + "phploc/phploc": "2.* || 3.* || 4.*", + "dompdf/dompdf":"0.8.*", "tecnickcom/tcpdf": "6.*", - "mpdf/mpdf": "5.*" + "mpdf/mpdf": "5.7.4 || 6.* || 7.*", + "php-coveralls/php-coveralls": "1.1.0 || ^2.0" }, "suggest": { "ext-zip": "Allows writing OOXML and ODF", @@ -60,5 +87,10 @@ "psr-4": { "PhpOffice\\PhpWord\\": "src/PhpWord" } + }, + "extra": { + "branch-alias": { + "dev-develop": "0.16-dev" + } } } diff --git a/docs/ISSUE_TEMPLATE.md b/docs/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..c7ed27d7 --- /dev/null +++ b/docs/ISSUE_TEMPLATE.md @@ -0,0 +1,35 @@ +This is: + +- [ ] a bug report +- [ ] a feature request +- [ ] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpword) + +### Expected Behavior + +Please describe the behavior you are expecting. + +### Current Behavior + +What is the current behavior? + +### Failure Information + +Please help provide information about the failure. + +### How to Reproduce + +Please provide a code sample that reproduces the issue. + +```php +addSection(); +$section->... +``` + +### Context + +* PHP version: +* PHPWord version: 0.14 diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..5430a996 --- /dev/null +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +### Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. + +Fixes # (issue) + +### Checklist: + +- [ ] I have run `composer run-script check --timeout=0` and no errors were reported +- [ ] The new code is covered by unit tests (check build/coverage for coverage report) +- [ ] I have updated the documentation to describe the changes diff --git a/docs/conf.py b/docs/conf.py index e9b1c59e..6b7cf8e8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,14 +41,14 @@ master_doc = 'index' # General information about the project. project = u'PHPWord' -copyright = u'2014-2015, PHPWord Contributors' +copyright = u'2014-2017, PHPWord Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.13.0' +version = '0.14.0' # The full version, including alpha/beta/rc tags. release = version @@ -120,7 +120,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +#html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. diff --git a/docs/containers.rst b/docs/containers.rst index f165a589..dc194d59 100644 --- a/docs/containers.rst +++ b/docs/containers.rst @@ -79,7 +79,7 @@ Below are the properties of the line numbering style. - ``start`` Line numbering starting value - ``increment`` Line number increments -- ``distance`` Distance between text and line numbering in twip +- ``distance`` Distance between text and line numbering in *twip* - ``restart`` Line numbering restart setting continuous\|newPage\|newSection @@ -98,6 +98,12 @@ that are available for the footer. See "Footer" section for detail. Additionally, only inside of the header reference you can add watermarks or background pictures. See "Watermarks" section. +You can pass an optional parameter to specify where the header/footer should be applied, it can be + +- ``Footer::AUTO`` default, all pages except if overridden by first or even +- ``Footer::FIRST`` each first page of the section +- ``Footer::EVEN`` each even page of the section. Will only be applied if the evenAndOddHeaders is set to true in phpWord->settings + Footers ------- diff --git a/docs/elements.rst b/docs/elements.rst index 27fd76b8..74b1d56f 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -31,7 +31,7 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 11 | Watermark | - | v | - | - | - | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ -| 12 | Object | v | v | v | v | v | v | +| 12 | OLEObject | v | v | v | v | v | v | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 13 | TOC | v | - | - | - | - | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ @@ -39,7 +39,7 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 15 | Endnote | v | - | - | v\*\* | v\*\* | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ -| 16 | CheckBox | v | v | v | v | - | - | +| 16 | CheckBox | v | v | v | v | v | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 17 | TextBox | v | v | v | v | - | - | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ @@ -47,6 +47,8 @@ column shows the containers while the rows lists the elements. +-------+-----------------+-----------+----------+----------+---------+------------+------------+ | 19 | Line | v | v | v | v | v | v | +-------+-----------------+-----------+----------+----------+---------+------------+------------+ +| 20 | Chart | v | | | v | | | ++-------+-----------------+-----------+----------+----------+---------+------------+------------+ Legend: @@ -75,11 +77,19 @@ italics, etc) or other elements, e.g. images or links. The syntaxes are as follo For available styling options see :ref:`font-style` and :ref:`paragraph-style`. +If you want to enable track changes on added text you can mark it as INSERTED or DELETED by a specific user at a given time: + +.. code-block:: php + + $text = $section->addText('Hello World!'); + $text->setChanged(\PhpOffice\PhpWord\Element\ChangedElement::TYPE_INSERTED, 'Fred', (new \DateTime())); + Titles ~~~~~~ If you want to structure your document or build table of contents, you need titles or headings. To add a title to the document, use the ``addTitleStyle`` and ``addTitle`` method. +If `depth` is 0, a Title will be inserted, otherwise a Heading1, Heading2, ... .. code-block:: php @@ -89,7 +99,7 @@ To add a title to the document, use the ``addTitleStyle`` and ``addTitle`` metho - ``depth``. - ``$fontStyle``. See :ref:`font-style`. - ``$paragraphStyle``. See :ref:`paragraph-style`. -- ``$text``. Text to be displayed in the document. +- ``$text``. Text to be displayed in the document. This can be `string` or a `\PhpOffice\PhpWord\Element\TextRun` It's necessary to add a title style to your document because otherwise the title won't be detected as a real title. @@ -135,12 +145,12 @@ Text breaks are empty new lines. To add text breaks, use the following syntax. A Page breaks ~~~~~~~~~~~ -There are two ways to insert a page breaks, using the ``addPageBreak`` +There are two ways to insert a page break, using the ``addPageBreak`` method or using the ``pageBreakBefore`` style of paragraph. -:: code-block:: php +.. code-block:: php - \\$section->addPageBreak(); + $section->addPageBreak(); Lists ----- @@ -159,7 +169,7 @@ Parameters: - ``$depth``. Depth of list item. - ``$fontStyle``. See :ref:`font-style`. - ``$listStyle``. List style of the current element TYPE\_NUMBER, - TYPE\_ALPHANUM, TYPE\_BULLET\_FILLED, etc. See list of constants in PHPWord\_Style\_ListItem. + TYPE\_ALPHANUM, TYPE\_BULLET\_FILLED, etc. See list of constants in PHPWord\\Style\\ListItem. - ``$paragraphStyle``. See :ref:`paragraph-style`. Advanced usage: @@ -232,7 +242,7 @@ To add an image, use the ``addImage`` method to sections, headers, footers, text $section->addImage($src, [$style]); -- ``$src``. String path to a local image, URL of a remote image or the image data, as a string. +- ``$src``. String path to a local image, URL of a remote image or the image data, as a string. Warning: Do not pass user-generated strings here, as that would allow an attacker to read arbitrary files or perform server-side request forgery by passing file paths or URLs instead of image data. - ``$style``. See :ref:`image-style`. Examples: @@ -274,11 +284,11 @@ Objects ------- You can add OLE embeddings, such as Excel spreadsheets or PowerPoint -presentations to the document by using ``addObject`` method. +presentations to the document by using ``addOLEObject`` method. .. code-block:: php - $section->addObject($src, [$style]); + $section->addOLEObject($src, [$style]); Table of contents ----------------- @@ -297,9 +307,9 @@ Your TOC can only be generated if you have add at least one title (See "Titles") Options for ``$tocStyle``: -- ``tabLeader``. Fill type between the title text and the page number. Use the defined constants in PHPWord\_Style\_TOC. -- ``tabPos``. The position of the tab where the page number appears in twips. -- ``indent``. The indent factor of the titles in twips. +- ``tabLeader``. Fill type between the title text and the page number. Use the defined constants in ``\PhpOffice\PhpWord\Style\TOC``. +- ``tabPos``. The position of the tab where the page number appears in *twip*. +- ``indent``. The indent factor of the titles in *twip*. Footnotes & endnotes -------------------- @@ -307,7 +317,7 @@ Footnotes & endnotes You can create footnotes with ``addFootnote`` and endnotes with ``addEndnote`` in texts or textruns, but it's recommended to use textrun to have better layout. You can use ``addText``, ``addLink``, -``addTextBreak``, ``addImage``, ``addObject`` on footnotes and endnotes. +``addTextBreak``, ``addImage``, ``addOLEObject`` on footnotes and endnotes. On textrun: @@ -333,11 +343,27 @@ On text: $footnote = $section->addFootnote(); $footnote->addText('Footnote text.'); -The footnote reference number will be displayed with decimal number -starting from 1. This number use ``FooterReference`` style which you can -redefine by ``addFontStyle`` method. Default value for this style is +By default the footnote reference number will be displayed with decimal number +starting from 1. This number uses the ``FooterReference`` style which you can +redefine with the ``addFontStyle`` method. Default value for this style is ``array('superScript' => true)``; +The footnote numbering can be controlled by setting the FootnoteProperties on the Section. + +.. code-block:: php + + $fp = new PhpWord\SimpleType\FootnoteProperties(); + //sets the position of the footnote (pageBottom (default), beneathText, sectEnd, docEnd) + $fp->setPos(FootnoteProperties::POSITION_DOC_END); + //set the number format to use (decimal (default), upperRoman, upperLetter, ...) + $fp->setNumFmt(FootnoteProperties::NUMBER_FORMAT_LOWER_ROMAN); + //force starting at other than 1 + $fp->setNumStart(2); + //when to restart counting (continuous (default), eachSect, eachPage) + $fp->setNumRestart(FootnoteProperties::RESTART_NUMBER_EACH_PAGE); + //And finaly, set it on the Section + $section->setFootnoteProperties($properties); + Checkboxes ---------- @@ -360,25 +386,118 @@ To be completed Fields ------ -To be completed +Currently the following fields are supported: + +- PAGE +- NUMPAGES +- DATE +- XE +- INDEX + +.. code-block:: php + + $section->addField($fieldType, [$properties], [$options], [$fieldText]) + +See ``\PhpOffice\PhpWord\Element\Field`` for list of properties and options available for each field type. +Options which are not specifically defined can be added. Those must start with a ``\``. + +For instance for the INDEX field, you can do the following (See `Index Field for list of available options `_ ): + +.. code-block:: php + + //the $fieldText can be either a simple string + $fieldText = 'The index value'; + + //or a 'TextRun', to be able to format the text you want in the index + $fieldText = new TextRun(); + $fieldText->addText('My '); + $fieldText->addText('bold index', ['bold' => true]); + $fieldText->addText(' entry'); + $section->addField('XE', array(), array(), $fieldText); + + //this actually adds the index + $section->addField('INDEX', array(), array('\\e " " \\h "A" \\c "3"'), 'right click to update index'); Line ------- +---- Line elements can be added to sections by using ``addLine``. .. code-block:: php - $linestyle = array('weight' => 1, 'width' => 100, 'height' => 0, 'color' => 635552); - $section->addLine($lineStyle) + $lineStyle = array('weight' => 1, 'width' => 100, 'height' => 0, 'color' => 635552); + $section->addLine($lineStyle); Available line style attributes: -- ``weight``. Line width in twips. +- ``weight``. Line width in *twip*. - ``color``. Defines the color of stroke. - ``dash``. Line types: dash, rounddot, squaredot, dashdot, longdash, longdashdot, longdashdotdot. - ``beginArrow``. Start type of arrow: block, open, classic, diamond, oval. - ``endArrow``. End type of arrow: block, open, classic, diamond, oval. -- ``width``. Line-object width in pt. -- ``height``. Line-object height in pt. -- ``flip``. Flip the line element: true, false. \ No newline at end of file +- ``width``. Line-object width in *pt*. +- ``height``. Line-object height in *pt*. +- ``flip``. Flip the line element: true, false. + +Chart +----- + +Charts can be added using + +.. code-block:: php + + $categories = array('A', 'B', 'C', 'D', 'E'); + $series = array(1, 3, 2, 5, 4); + $chart = $section->addChart('line', $categories, $series, $style); + +For available styling options see :ref:`chart-style`. + +check out the Sample_32_Chart.php for more options and styling. + +Comments +-------- + +Comments can be added to a document by using ``addComment``. +The comment can contain formatted text. Once the comment has been added, it can be linked to any element with ``setCommentStart``. + +.. code-block:: php + + // first create a comment + $comment= new \PhpOffice\PhpWord\Element\Comment('Authors name', new \DateTime(), 'my_initials'); + $comment->addText('Test', array('bold' => true)); + + // add it to the document + $phpWord->addComment($comment); + + $textrun = $section->addTextRun(); + $textrun->addText('This '); + $text = $textrun->addText('is'); + // link the comment to the text you just created + $text->setCommentStart($comment); + +If no end is set for a comment using the ``setCommentEnd``, the comment will be ended automatically at the end of the element it is started on. + +Track Changes +------------- + +Track changes can be set on text elements. There are 2 ways to set the change information on an element. +Either by calling the `setChangeInfo()`, or by setting the `TrackChange` instance on the element with `setTrackChange()`. + +.. code-block:: php + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + + // New portrait section + $section = $phpWord->addSection(); + $textRun = $section->addTextRun(); + + $text = $textRun->addText('Hello World! Time to '); + + $text = $textRun->addText('wake ', array('bold' => true)); + $text->setChangeInfo(TrackChange::INSERTED, 'Fred', time() - 1800); + + $text = $textRun->addText('up'); + $text->setTrackChange(new TrackChange(TrackChange::INSERTED, 'Fred')); + + $text = $textRun->addText('go to sleep'); + $text->setChangeInfo(TrackChange::DELETED, 'Barney', new \DateTime('@' . (time() - 3600))); diff --git a/docs/general.rst b/docs/general.rst index 27d0448a..f40a08c3 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -80,8 +80,8 @@ folder `__. /* Note: we skip RTF, because it's not XML-based and requires a different example. */ /* Note: we skip PDF, because "HTML-to-PDF" approach is used to create PDF documents. */ -Settings --------- +PHPWord Settings +---------------- The ``PhpOffice\PhpWord\Settings`` class provides some options that will affect the behavior of PHPWord. Below are the options. @@ -109,8 +109,8 @@ Zip class By default, PHPWord uses `Zip extension `__ to deal with ZIP compressed archives and files inside them. If you can't have Zip extension installed on your server, you can use pure PHP library -alternative, `PclZip `__, which -included with PHPWord. +alternative, `PclZip `__, which is +included in PHPWord. .. code-block:: php @@ -141,6 +141,93 @@ default font by using the following two functions: $phpWord->setDefaultFontName('Times New Roman'); $phpWord->setDefaultFontSize(12); +Document settings +----------------- +Settings for the generated document can be set using ``$phpWord->getSettings()`` + +Magnification Setting +~~~~~~~~~~~~~~~~~~~~~ +The default zoom value is 100 percent. This can be changed either to another percentage + +.. code-block:: php + + $phpWord->getSettings()->setZoom(75); + +Or to predefined values ``fullPage``, ``bestFit``, ``textFit`` + +.. code-block:: php + + $phpWord->getSettings()->setZoom(Zoom::BEST_FIT); + +Mirroring the Page Margins +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use mirror margins to set up facing pages for double-sided documents, such as books or magazines. + +.. code-block:: php + + $phpWord->getSettings()->setMirrorMargins(true); + +Spelling and grammatical checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default spelling and grammatical errors are shown as soon as you open a word document. +For big documents this can slow down the opening of the document. You can hide the spelling and/or grammatical errors with: + +.. code-block:: php + + $phpWord->getSettings()->setHideGrammaticalErrors(true); + $phpWord->getSettings()->setHideSpellingErrors(true); + +You can also specify the status of the spell and grammar checks, marking spelling or grammar as dirty will force a re-check when opening the document. + +.. code-block:: php + + $proofState = new ProofState(); + $proofState->setGrammar(ProofState::CLEAN); + $proofState->setSpelling(ProofState::DIRTY); + + $phpWord->getSettings()->setProofState(proofState); + +Track Revisions +~~~~~~~~~~~~~~~ +Track changes can be activated using ``setTrackRevisions``, you can furture specify + +- Not to use move syntax, instead moved items will be seen as deleted in one place and added in another +- Not track formatting revisions + +.. code-block:: php + + $phpWord->getSettings()->setTrackRevisions(true); + $phpWord->getSettings()->setDoNotTrackMoves(true); + $phpWord->getSettings()->setDoNotTrackFormatting(true); + +Decimal Symbol +~~~~~~~~~~~~~~ +The default symbol to represent a decimal figure is the ``.`` in english. In french you might want to change it to ``,`` for instance. + +.. code-block:: php + + $phpWord->getSettings()->setDecimalSymbol(','); + +Document Language +~~~~~~~~~~~~~~~~~ +The default language of the document can be change with the following. + +.. code-block:: php + + $phpWord->getSettings()->setThemeFontLang(new Language(Language::FR_BE)); + +``Language`` has 3 parameters, one for Latin languages, one for East Asian languages and one for Complex (Bi-Directional) languages. +A couple of language codes are provided in the ``PhpOffice\PhpWord\ComplexType\Language`` class but any valid code/ID can be used. + +In case you are generating an RTF document the language need to be set differently. + +.. code-block:: php + + $lang = new Language(); + $lang->setLangId(Language::EN_GB_ID); + $phpWord->getSettings()->setThemeFontLang($lang); + Document information -------------------- @@ -168,7 +255,7 @@ The base length unit in Open Office XML is twip. Twip means "TWentieth of an Inch Point", i.e. 1 twip = 1/1440 inch. You can use PHPWord helper functions to convert inches, centimeters, or -points to twips. +points to twip. .. code-block:: php @@ -183,3 +270,65 @@ points to twips. $sectionStyle->setMarginLeft(\PhpOffice\PhpWord\Shared\Converter::inchToTwip(.5)); // 2 cm right margin $sectionStyle->setMarginRight(\PhpOffice\PhpWord\Shared\Converter::cmToTwip(2)); + +Document protection +------------------- + +The document (or parts of it) can be password protected. + +.. code-block:: php + + $documentProtection = $phpWord->getSettings()->getDocumentProtection(); + $documentProtection->setEditing(DocProtect::READ_ONLY); + $documentProtection->setPassword('myPassword'); + +Automatically Recalculate Fields on Open +---------------------------------------- + +To force an update of the fields present in the document, set updateFields to true + +.. code-block:: php + + $phpWord->getSettings()->setUpdateFields(true); + +Hyphenation +----------- +Hyphenation describes the process of breaking words with hyphens. There are several options to control hyphenation. + +Auto hyphenation +~~~~~~~~~~~~~~~~ + +To automatically hyphenate text set ``autoHyphenation`` to ``true``. + +.. code-block:: php + + $phpWord->getSettings()->setAutoHyphenation(true); + +Consecutive Hyphen Limit +~~~~~~~~~~~~~~~~~~~~~~~~ + +The maximum number of consecutive lines of text ending with a hyphen can be controlled by the ``consecutiveHyphenLimit`` option. +There is no limit if the option is not set or the provided value is ``0``. + +.. code-block:: php + + $phpWord->getSettings()->setConsecutiveHyphenLimit(2); + +Hyphenation Zone +~~~~~~~~~~~~~~~~ + +The hyphenation zone (in *twip*) is the allowed amount of whitespace before hyphenation is applied. +The smaller the hyphenation zone the more words are hyphenated. Or in other words, the wider the hyphenation zone the less words are hyphenated. + +.. code-block:: php + + $phpWord->getSettings()->setHyphenationZone(\PhpOffice\PhpWord\Shared\Converter::cmToTwip(1)); + +Hyphenate Caps +~~~~~~~~~~~~~~ + +To control whether or not words in all capital letters shall be hyphenated use the `doNotHyphenateCaps` option. + +.. code-block:: php + + $phpWord->getSettings()->setDoNotHyphenateCaps(true); diff --git a/docs/installing.rst b/docs/installing.rst index 9593484a..34353be8 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -34,7 +34,7 @@ Example: { "require": { - "phpoffice/phpword": "v0.13.*" + "phpoffice/phpword": "v0.14.*" } } @@ -51,11 +51,8 @@ Example: } } - Using samples ------------- -After installation, you can browse and use the samples that we've -provided, either by command line or using browser. If you can access -your PHPWord library folder using browser, point your browser to the -``samples`` folder, e.g. ``http://localhost/PhpWord/samples/``. +More examples are provided in the ``samples`` directory. +For an easy access to those samples launch ``php -S localhost:8000`` in the samples directory then browse to http://localhost:8000 to view the samples. diff --git a/docs/intro.rst b/docs/intro.rst index d1c791cf..d88cd626 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -49,6 +49,7 @@ Features - Insert drawing shapes (arc, curve, line, polyline, rect, oval) - Insert charts (pie, doughnut, bar, line, area, scatter, radar) - Insert form fields (textinput, checkbox, and dropdown) +- Insert comments - Create document from templates - Use XSL 1.0 style sheets to transform headers, main document part, and footers of an OOXML template - ... and many more features on progress @@ -102,6 +103,8 @@ Writers +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | Endnote | ✓ | | | ✓ | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ +| | Comments | ✓ | | | | | ++---------------------------+----------------------+--------+-------+-------+--------+-------+ | **Graphs** | 2D basic graphs | ✓ | | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | 2D advanced graphs | | | | | | @@ -161,6 +164,8 @@ Readers +---------------------------+----------------------+--------+-------+-------+-------+-------+ | | Endnote | ✓ | | | | | +---------------------------+----------------------+--------+-------+-------+-------+-------+ +| | Comments | | | | | | ++---------------------------+----------------------+--------+-------+-------+-------+-------+ | **Graphs** | 2D basic graphs | | | | | | +---------------------------+----------------------+--------+-------+-------+-------+-------+ | | 2D advanced graphs | | | | | | diff --git a/docs/references.rst b/docs/references.rst index 9c4e06a8..a17f558d 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -4,7 +4,7 @@ References ========== ISO/IEC 29500, Third edition, 2012-09-01 ---------------------- +---------------------------------------- - `Part 1: Fundamentals and Markup Language Reference `__ diff --git a/docs/styles.rst b/docs/styles.rst index b71059a6..f8d26a9b 100644 --- a/docs/styles.rst +++ b/docs/styles.rst @@ -11,26 +11,27 @@ Section Available Section style options: - ``borderBottomColor``. Border bottom color. -- ``borderBottomSize``. Border bottom size (in twips). +- ``borderBottomSize``. Border bottom size in *twip*. - ``borderLeftColor``. Border left color. -- ``borderLeftSize``. Border left size (in twips). +- ``borderLeftSize``. Border left size in *twip*. - ``borderRightColor``. Border right color. -- ``borderRightSize``. Border right size (in twips). +- ``borderRightSize``. Border right size in *twip*. - ``borderTopColor``. Border top color. -- ``borderTopSize``. Border top size (in twips). +- ``borderTopSize``. Border top size in *twip*. - ``breakType``. Section break type (nextPage, nextColumn, continuous, evenPage, oddPage). - ``colsNum``. Number of columns. - ``colsSpace``. Spacing between columns. - ``footerHeight``. Spacing to bottom of footer. - ``gutter``. Page gutter spacing. - ``headerHeight``. Spacing to top of header. -- ``marginTop``. Page margin top (in twips). -- ``marginLeft``. Page margin left (in twips). -- ``marginRight``. Page margin right (in twips). -- ``marginBottom``. Page margin bottom (in twips). +- ``marginTop``. Page margin top in *twip*. +- ``marginLeft``. Page margin left in *twip*. +- ``marginRight``. Page margin right in *twip*. +- ``marginBottom``. Page margin bottom in *twip*. - ``orientation``. Page orientation (``portrait``, which is default, or ``landscape``). -- ``pageSizeH``. Page height (in twips). Implicitly defined by ``orientation`` option. Any changes are discouraged. -- ``pageSizeW``. Page width (in twips). Implicitly defined by ``orientation`` option. Any changes are discouraged. + See ``\PhpOffice\PhpWord\Style\Section::ORIENTATION_...`` class constants for possible values +- ``pageSizeH``. Page height in *twip*. Implicitly defined by ``orientation`` option. Any changes are discouraged. +- ``pageSizeW``. Page width in *twip*. Implicitly defined by ``orientation`` option. Any changes are discouraged. .. _font-style: @@ -45,6 +46,7 @@ Available Font style options: - ``color``. Font color, e.g. *FF0000*. - ``doubleStrikethrough``. Double strikethrough, *true* or *false*. - ``fgColor``. Font highlight color, e.g. *yellow*, *green*, *blue*. + See ``\PhpOffice\PhpWord\Style\Font::FGCOLOR_...`` class constants for possible values - ``hint``. Font content type, *default*, *eastAsia*, or *cs*. - ``italic``. Italic, *true* or *false*. - ``name``. Font name, e.g. *Arial*. @@ -54,7 +56,11 @@ Available Font style options: - ``strikethrough``. Strikethrough, *true* or *false*. - ``subScript``. Subscript, *true* or *false*. - ``superScript``. Superscript, *true* or *false*. -- ``underline``. Underline, *dash*, *dotted*, etc. +- ``underline``. Underline, *single*, *dash*, *dotted*, etc. + See ``\PhpOffice\PhpWord\Style\Font::UNDERLINE_...`` class constants for possible values +- ``lang``. Language, either a language code like *en-US*, *fr-BE*, etc. or an object (or as an array) if you need to set eastAsian or bidirectional languages + See ``\PhpOffice\PhpWord\Style\Language`` class for some language codes. +- ``position``. The text position, raised or lowered, in half points .. _paragraph-style: @@ -64,19 +70,28 @@ Paragraph Available Paragraph style options: - ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012. -See ``\PhpOffice\PhpWord\SimpleType\Jc`` class for the details. + See ``\PhpOffice\PhpWord\SimpleType\Jc`` class constants for possible values. - ``basedOn``. Parent style. -- ``hanging``. Hanging by how much. -- ``indent``. Indent by how much. +- ``hanging``. Hanging in *twip*. +- ``indent``. Indent in *twip*. - ``keepLines``. Keep all lines on one page, *true* or *false*. - ``keepNext``. Keep paragraph with next paragraph, *true* or *false*. - ``lineHeight``. Text line height, e.g. *1.0*, *1.5*, etc. - ``next``. Style for next paragraph. - ``pageBreakBefore``. Start paragraph on next page, *true* or *false*. -- ``spaceBefore``. Space before paragraph. -- ``spaceAfter``. Space after paragraph. +- ``spaceBefore``. Space before paragraph in *twip*. +- ``spaceAfter``. Space after paragraph in *twip*. +- ``spacing``. Space between lines. +- ``spacingLineRule``. Line Spacing Rule. *auto*, *exact*, *atLeast* + See ``\PhpOffice\PhpWord\SimpleType\LineSpacingRule`` class constants for possible values. +- ``suppressAutoHyphens``. Hyphenation for paragraph, *true* or *false*. - ``tabs``. Set of custom tab stops. - ``widowControl``. Allow first/last line to display on a separate page, *true* or *false*. +- ``contextualSpacing``. Ignore Spacing Above and Below When Using Identical Styles, *true* or *false*. +- ``bidi``. Right to Left Paragraph Layout, *true* or *false*. +- ``shading``. Paragraph Shading. +- ``textAlignment``. Vertical Character Alignment on Line. + See ``\PhpOffice\PhpWord\SimpleType\TextAlignment`` class constants for possible values. .. _table-style: @@ -86,12 +101,30 @@ Table Available Table style options: - ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012. -See ``\PhpOffice\PhpWord\SimpleType\JcTable`` and ``\PhpOffice\PhpWord\SimpleType\Jc`` classes for the details. + See ``\PhpOffice\PhpWord\SimpleType\JcTable`` and ``\PhpOffice\PhpWord\SimpleType\Jc`` class constants for possible values. - ``bgColor``. Background color, e.g. '9966CC'. - ``border(Top|Right|Bottom|Left)Color``. Border color, e.g. '9966CC'. -- ``border(Top|Right|Bottom|Left)Size``. Border size in twips. -- ``cellMargin(Top|Right|Bottom|Left)``. Cell margin in twips. +- ``border(Top|Right|Bottom|Left)Size``. Border size in *twip*. +- ``cellMargin(Top|Right|Bottom|Left)``. Cell margin in *twip*. +- ``indent``. Table indent from leading margin. Must be an instance of ``\PhpOffice\PhpWord\ComplexType\TblWidth``. - ``width``. Table width in percent. +- ``unit``. The unit to use for the width. One of ``\PhpOffice\PhpWord\SimpleType\TblWidth``. Defaults to *auto*. +- ``layout``. Table layout, either *fixed* or *autofit* See ``\PhpOffice\PhpWord\Style\Table`` for constants. +- ``cellSpacing`` Cell spacing in *twip* +- ``position`` Floating Table Positioning, see below for options + +Floating Table Positioning options: + +- ``leftFromText`` Distance From Left of Table to Text in *twip* +- ``rightFromText`` Distance From Right of Table to Text in *twip* +- ``topFromText`` Distance From Top of Table to Text in *twip* +- ``bottomFromText`` Distance From Top of Table to Text in *twip* +- ``vertAnchor`` Table Vertical Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::VANCHOR_*`` +- ``horzAnchor`` Table Horizontal Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::HANCHOR_*`` +- ``tblpXSpec`` Relative Horizontal Alignment From Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::XALIGN_*`` +- ``tblpX`` Absolute Horizontal Distance From Anchorin *twip* +- ``tblpYSpec`` Relative Vertical Alignment From Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::YALIGN_*`` +- ``tblpY`` Absolute Vertical Distance From Anchorin *twip* Available Row style options: @@ -103,12 +136,13 @@ Available Cell style options: - ``bgColor``. Background color, e.g. '9966CC'. - ``border(Top|Right|Bottom|Left)Color``. Border color, e.g. '9966CC'. -- ``border(Top|Right|Bottom|Left)Size``. Border size in twips. +- ``border(Top|Right|Bottom|Left)Size``. Border size in *twip*. - ``gridSpan``. Number of columns spanned. -- ``textDirection(btLr|tbRl)``. Direction of text. You can use constants ``\PhpOffice\PhpWord\Style\Cell::TEXT_DIR_BTLR`` and ``\PhpOffice\PhpWord\Style\Cell::TEXT_DIR_TBRL`` +- ``textDirection(btLr|tbRl)``. Direction of text. + You can use constants ``\PhpOffice\PhpWord\Style\Cell::TEXT_DIR_BTLR`` and ``\PhpOffice\PhpWord\Style\Cell::TEXT_DIR_TBRL`` - ``valign``. Vertical alignment, *top*, *center*, *both*, *bottom*. - ``vMerge``. *restart* or *continue*. -- ``width``. Cell width in twips. +- ``width``. Cell width in *twip*. .. _image-style: @@ -118,11 +152,15 @@ Image Available Image style options: - ``alignment``. See ``\PhpOffice\PhpWord\SimpleType\Jc`` class for the details. -- ``height``. Height in pixels. +- ``height``. Height in *pt*. - ``marginLeft``. Left margin in inches, can be negative. - ``marginTop``. Top margin in inches, can be negative. -- ``width``. Width in pixels. +- ``width``. Width in *pt*. - ``wrappingStyle``. Wrapping style, *inline*, *square*, *tight*, *behind*, or *infront*. +- ``wrapDistanceTop``. Top text wrapping in pixels. +- ``wrapDistanceBottom``. Bottom text wrapping in pixels. +- ``wrapDistanceLeft``. Left text wrapping in pixels. +- ``wrapDistanceRight``. Right text wrapping in pixels. .. _numbering-level-style: @@ -132,7 +170,7 @@ Numbering level Available NumberingLevel style options: - ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012. -See ``\PhpOffice\PhpWord\SimpleType\Jc`` class for the details. + See ``\PhpOffice\PhpWord\SimpleType\Jc`` class constants for possible values. - ``font``. Font name. - ``format``. Numbering format bullet\|decimal\|upperRoman\|lowerRoman\|upperLetter\|lowerLetter. - ``hanging``. See paragraph style. @@ -143,3 +181,25 @@ See ``\PhpOffice\PhpWord\SimpleType\Jc`` class for the details. - ``suffix``. Content between numbering symbol and paragraph text tab\|space\|nothing. - ``tabPos``. See paragraph style. - ``text``. Numbering level text e.g. %1 for nonbullet or bullet character. + +.. _chart-style: + +Chart +----- + +Available Chart style options: + +- ``width``. Width (in EMU). +- ``height``. Height (in EMU). +- ``3d``. Is 3D; applies to pie, bar, line, area, *true* or *false*. +- ``colors``. A list of colors to use in the chart. +- ``title``. The title for the chart. +- ``showLegend``. Show legend, *true* or *false*. +- ``categoryLabelPosition``. Label position for categories, *nextTo* (default), *low* or *high*. +- ``valueLabelPosition``. Label position for values, *nextTo* (default), *low* or *high*. +- ``categoryAxisTitle``. The title for the category axis. +- ``valueAxisTitle``. The title for the values axis. +- ``majorTickMarkPos``. The position for major tick marks, *in*, *out*, *cross*, *none* (default). +- ``showAxisLabels``. Show labels for axis, *true* or *false*. +- ``gridX``. Show Gridlines for X-Axis, *true* or *false*. +- ``gridY``. Show Gridlines for Y-Axis, *true* or *false*. diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..666c63b9 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,13 @@ +includes: + - vendor/phpstan/phpstan/conf/config.level1.neon +parameters: + memory-limit: 20000000 + autoload_directories: + - tests + autoload_files: + - tests/bootstrap.php + excludes_analyse: + - */pclzip.lib.php + - src/PhpWord/Shared/OLERead.php + - src/PhpWord/Reader/MsDoc.php + - src/PhpWord/Writer/PDF/MPDF.php \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 015dd2ed..4a882446 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,8 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" - syntaxCheck="false"> + stopOnFailure="false"> ./tests/PhpWord @@ -22,7 +21,8 @@ - + + \ No newline at end of file diff --git a/samples/Sample_01_SimpleText.php b/samples/Sample_01_SimpleText.php index 1e51b2c0..8af44d20 100644 --- a/samples/Sample_01_SimpleText.php +++ b/samples/Sample_01_SimpleText.php @@ -1,9 +1,15 @@ getSettings()->setThemeFontLang($languageEnGb); $fontStyleName = 'rStyle'; $phpWord->addFontStyle($fontStyleName, array('bold' => true, 'italic' => true, 'size' => 16, 'allCaps' => true, 'doubleStrikethrough' => true)); @@ -20,6 +26,10 @@ $section = $phpWord->addSection(); $section->addTitle('Welcome to PhpWord', 1); $section->addText('Hello World!'); +// $pStyle = new Font(); +// $pStyle->setLang() +$section->addText('Ce texte-ci est en français.', array('lang' => \PhpOffice\PhpWord\Style\Language::FR_BE)); + // Two text break $section->addTextBreak(2); diff --git a/samples/Sample_02_TabStops.php b/samples/Sample_02_TabStops.php index 17021987..4f588ee7 100644 --- a/samples/Sample_02_TabStops.php +++ b/samples/Sample_02_TabStops.php @@ -14,7 +14,7 @@ $phpWord->addParagraphStyle( new \PhpOffice\PhpWord\Style\Tab('left', 1550), new \PhpOffice\PhpWord\Style\Tab('center', 3200), new \PhpOffice\PhpWord\Style\Tab('right', 5300), - ) + ), ) ); diff --git a/samples/Sample_06_Footnote.php b/samples/Sample_06_Footnote.php index 30afcf81..19d6a524 100644 --- a/samples/Sample_06_Footnote.php +++ b/samples/Sample_06_Footnote.php @@ -1,4 +1,7 @@ addText('But you can only put footnote in section, not in header or f $section->addText( 'You can also create the footnote directly from the section making it wrap in a paragraph ' - . 'like the footnote below this paragraph. But is is best used from within a textrun.' + . 'like the footnote below this paragraph. But is best used from within a textrun.' ); $footnote = $section->addFootnote(); $footnote->addText('The reference for this is wrapped in its own line'); +$footnoteProperties = new FootnoteProperties(); +$footnoteProperties->setNumFmt(NumberFormat::DECIMAL_ENCLOSED_CIRCLE); +$section->setFootnoteProperties($footnoteProperties); + // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); if (!CLI) { diff --git a/samples/Sample_07_TemplateCloneRow.php b/samples/Sample_07_TemplateCloneRow.php index 22a68537..81253d0a 100644 --- a/samples/Sample_07_TemplateCloneRow.php +++ b/samples/Sample_07_TemplateCloneRow.php @@ -1,62 +1,62 @@ -setValue('weekday', date('l')); // On section/content -$templateProcessor->setValue('time', date('H:i')); // On footer -$templateProcessor->setValue('serverName', realpath(__DIR__)); // On header - -// Simple table -$templateProcessor->cloneRow('rowValue', 10); - -$templateProcessor->setValue('rowValue#1', 'Sun'); -$templateProcessor->setValue('rowValue#2', 'Mercury'); -$templateProcessor->setValue('rowValue#3', 'Venus'); -$templateProcessor->setValue('rowValue#4', 'Earth'); -$templateProcessor->setValue('rowValue#5', 'Mars'); -$templateProcessor->setValue('rowValue#6', 'Jupiter'); -$templateProcessor->setValue('rowValue#7', 'Saturn'); -$templateProcessor->setValue('rowValue#8', 'Uranus'); -$templateProcessor->setValue('rowValue#9', 'Neptun'); -$templateProcessor->setValue('rowValue#10', 'Pluto'); - -$templateProcessor->setValue('rowNumber#1', '1'); -$templateProcessor->setValue('rowNumber#2', '2'); -$templateProcessor->setValue('rowNumber#3', '3'); -$templateProcessor->setValue('rowNumber#4', '4'); -$templateProcessor->setValue('rowNumber#5', '5'); -$templateProcessor->setValue('rowNumber#6', '6'); -$templateProcessor->setValue('rowNumber#7', '7'); -$templateProcessor->setValue('rowNumber#8', '8'); -$templateProcessor->setValue('rowNumber#9', '9'); -$templateProcessor->setValue('rowNumber#10', '10'); - -// Table with a spanned cell -$templateProcessor->cloneRow('userId', 3); - -$templateProcessor->setValue('userId#1', '1'); -$templateProcessor->setValue('userFirstName#1', 'James'); -$templateProcessor->setValue('userName#1', 'Taylor'); -$templateProcessor->setValue('userPhone#1', '+1 428 889 773'); - -$templateProcessor->setValue('userId#2', '2'); -$templateProcessor->setValue('userFirstName#2', 'Robert'); -$templateProcessor->setValue('userName#2', 'Bell'); -$templateProcessor->setValue('userPhone#2', '+1 428 889 774'); - -$templateProcessor->setValue('userId#3', '3'); -$templateProcessor->setValue('userFirstName#3', 'Michael'); -$templateProcessor->setValue('userName#3', 'Ray'); -$templateProcessor->setValue('userPhone#3', '+1 428 889 775'); - -echo date('H:i:s'), ' Saving the result document...', EOL; -$templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx'); - -echo getEndingNotes(array('Word2007' => 'docx')); -if (!CLI) { - include_once 'Sample_Footer.php'; -} +setValue('weekday', date('l')); // On section/content +$templateProcessor->setValue('time', date('H:i')); // On footer +$templateProcessor->setValue('serverName', realpath(__DIR__)); // On header + +// Simple table +$templateProcessor->cloneRow('rowValue', 10); + +$templateProcessor->setValue('rowValue#1', 'Sun'); +$templateProcessor->setValue('rowValue#2', 'Mercury'); +$templateProcessor->setValue('rowValue#3', 'Venus'); +$templateProcessor->setValue('rowValue#4', 'Earth'); +$templateProcessor->setValue('rowValue#5', 'Mars'); +$templateProcessor->setValue('rowValue#6', 'Jupiter'); +$templateProcessor->setValue('rowValue#7', 'Saturn'); +$templateProcessor->setValue('rowValue#8', 'Uranus'); +$templateProcessor->setValue('rowValue#9', 'Neptun'); +$templateProcessor->setValue('rowValue#10', 'Pluto'); + +$templateProcessor->setValue('rowNumber#1', '1'); +$templateProcessor->setValue('rowNumber#2', '2'); +$templateProcessor->setValue('rowNumber#3', '3'); +$templateProcessor->setValue('rowNumber#4', '4'); +$templateProcessor->setValue('rowNumber#5', '5'); +$templateProcessor->setValue('rowNumber#6', '6'); +$templateProcessor->setValue('rowNumber#7', '7'); +$templateProcessor->setValue('rowNumber#8', '8'); +$templateProcessor->setValue('rowNumber#9', '9'); +$templateProcessor->setValue('rowNumber#10', '10'); + +// Table with a spanned cell +$templateProcessor->cloneRow('userId', 3); + +$templateProcessor->setValue('userId#1', '1'); +$templateProcessor->setValue('userFirstName#1', 'James'); +$templateProcessor->setValue('userName#1', 'Taylor'); +$templateProcessor->setValue('userPhone#1', '+1 428 889 773'); + +$templateProcessor->setValue('userId#2', '2'); +$templateProcessor->setValue('userFirstName#2', 'Robert'); +$templateProcessor->setValue('userName#2', 'Bell'); +$templateProcessor->setValue('userPhone#2', '+1 428 889 774'); + +$templateProcessor->setValue('userId#3', '3'); +$templateProcessor->setValue('userFirstName#3', 'Michael'); +$templateProcessor->setValue('userName#3', 'Ray'); +$templateProcessor->setValue('userPhone#3', '+1 428 889 775'); + +echo date('H:i:s'), ' Saving the result document...', EOL; +$templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx'); + +echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_07_TemplateCloneRow.docx'); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_08_ParagraphPagination.php b/samples/Sample_08_ParagraphPagination.php index f91b1c56..3c21b138 100644 --- a/samples/Sample_08_ParagraphPagination.php +++ b/samples/Sample_08_ParagraphPagination.php @@ -19,7 +19,7 @@ $section->addText( 'Below are the samples on how to control your paragraph ' . 'pagination. See "Line and Page Break" tab on paragraph properties ' . 'window to see the attribute set by these controls.', - array('bold' => true), + array('bold' => true), array('space' => array('before' => 360, 'after' => 480)) ); diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index 53d32e08..32d89573 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -1,4 +1,7 @@ addTextBreak(1); $section->addText('Fancy table', $header); $fancyTableStyleName = 'Fancy Table'; -$fancyTableStyle = array('borderSize' => 6, 'borderColor' => '006699', 'cellMargin' => 80, 'alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER); +$fancyTableStyle = array('borderSize' => 6, 'borderColor' => '006699', 'cellMargin' => 80, 'alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER, 'cellSpacing' => 50); $fancyTableFirstRowStyle = array('borderBottomSize' => 18, 'borderBottomColor' => '0000FF', 'bgColor' => '66BBFF'); $fancyTableCellStyle = array('valign' => 'center'); $fancyTableCellBtlrStyle = array('valign' => 'center', 'textDirection' => \PhpOffice\PhpWord\Style\Cell::TEXT_DIR_BTLR); @@ -46,11 +49,11 @@ for ($i = 1; $i <= 8; $i++) { $table->addCell(2000)->addText("Cell {$i}"); $table->addCell(2000)->addText("Cell {$i}"); $table->addCell(2000)->addText("Cell {$i}"); - $text = (0== $i % 2) ? 'X' : ''; + $text = (0 == $i % 2) ? 'X' : ''; $table->addCell(500)->addText($text); } -/** +/* * 3. colspan (gridSpan) and rowspan (vMerge) * --------------------- * | | B | | @@ -93,7 +96,7 @@ $table->addCell(2000, $cellVCentered)->addText('C', null, $cellHCentered); $table->addCell(2000, $cellVCentered)->addText('D', null, $cellHCentered); $table->addCell(null, $cellRowContinue); -/** +/* * 4. colspan (gridSpan) and rowspan (vMerge) * --------------------- * | | B | 1 | @@ -104,28 +107,29 @@ $table->addCell(null, $cellRowContinue); * --------------------- * @see https://github.com/PHPOffice/PHPWord/issues/806 */ + $section->addPageBreak(); $section->addText('Table with colspan and rowspan', $header); -$styleTable = ['borderSize' => 6, 'borderColor' => '999999']; +$styleTable = array('borderSize' => 6, 'borderColor' => '999999'); $phpWord->addTableStyle('Colspan Rowspan', $styleTable); $table = $section->addTable('Colspan Rowspan'); $row = $table->addRow(); - -$row->addCell(null, ['vMerge' => 'restart'])->addText('A'); -$row->addCell(null, ['gridSpan' => 2, 'vMerge' => 'restart',])->addText('B'); -$row->addCell()->addText('1'); +$row->addCell(1000, array('vMerge' => 'restart'))->addText('A'); +$row->addCell(1000, array('gridSpan' => 2, 'vMerge' => 'restart'))->addText('B'); +$row->addCell(1000)->addText('1'); $row = $table->addRow(); -$row->addCell(null, ['vMerge' => 'continue']); -$row->addCell(null, ['vMerge' => 'continue','gridSpan' => 2,]); -$row->addCell()->addText('2'); +$row->addCell(1000, array('vMerge' => 'continue')); +$row->addCell(1000, array('vMerge' => 'continue', 'gridSpan' => 2)); +$row->addCell(1000)->addText('2'); + $row = $table->addRow(); -$row->addCell(null, ['vMerge' => 'continue']); -$row->addCell()->addText('C'); -$row->addCell()->addText('D'); -$row->addCell()->addText('3'); +$row->addCell(1000, array('vMerge' => 'continue')); +$row->addCell(1000)->addText('C'); +$row->addCell(1000)->addText('D'); +$row->addCell(1000)->addText('3'); // 5. Nested table @@ -138,6 +142,15 @@ $cell->addText('This cell contains nested table.'); $innerCell = $cell->addTable(array('alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER))->addRow()->addCell(); $innerCell->addText('Inside nested table'); +// 6. Table with floating position + +$section->addTextBreak(2); +$section->addText('Table with floating positioning.', $header); + +$table = $section->addTable(array('borderSize' => 6, 'borderColor' => '999999', 'position' => array('vertAnchor' => TablePosition::VANCHOR_TEXT, 'bottomFromText' => Converter::cmToTwip(1)))); +$cell = $table->addRow()->addCell(); +$cell->addText('This is a single cell.'); + // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); if (!CLI) { diff --git a/samples/Sample_10_EastAsianFontStyle.php b/samples/Sample_10_EastAsianFontStyle.php index 2541af86..87345ae0 100644 --- a/samples/Sample_10_EastAsianFontStyle.php +++ b/samples/Sample_10_EastAsianFontStyle.php @@ -7,7 +7,7 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord(); $section = $phpWord->addSection(); $header = array('size' => 16, 'bold' => true); //1.Use EastAisa FontStyle -$section->addText('中文楷体样式测试', array('name' => '楷体', 'size' => 16, 'color' => '1B2232')); +$section->addText('中文楷体样式测试', array('name' => '楷体', 'size' => 16, 'color' => '1B2232', 'lang' => array('latin' => 'en-US', 'eastAsia' => 'zh-CN'))); // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); diff --git a/samples/Sample_13_Images.php b/samples/Sample_13_Images.php index a3c2af23..f7be3be9 100644 --- a/samples/Sample_13_Images.php +++ b/samples/Sample_13_Images.php @@ -1,4 +1,7 @@ addSection(); $section->addText('Local image without any styles:'); $section->addImage('resources/_mars.jpg'); -$section->addTextBreak(2); +printSeparator($section); $section->addText('Local image with styles:'); $section->addImage('resources/_earth.jpg', array('width' => 210, 'height' => 210, 'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER)); -$section->addTextBreak(2); // Remote image +printSeparator($section); $source = 'http://php.net/images/logos/php-med-trans-light.gif'; $section->addText("Remote image from: {$source}"); $section->addImage($source); +// Image from string +printSeparator($section); +$source = 'resources/_mars.jpg'; +$fileContent = file_get_contents($source); +$section->addText('Image from string'); +$section->addImage($fileContent); + //Wrapping style -$text = str_repeat('Hello World! ', 15); +printSeparator($section); +$text = str_repeat('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ', 2); $wrappingStyles = array('inline', 'behind', 'infront', 'square', 'tight'); foreach ($wrappingStyles as $wrappingStyle) { - $section->addTextBreak(5); $section->addText("Wrapping style {$wrappingStyle}"); $section->addImage( 'resources/_earth.jpg', array( - 'positioning' => 'relative', - 'marginTop' => -1, - 'marginLeft' => 1, - 'width' => 80, - 'height' => 80, - 'wrappingStyle' => $wrappingStyle, + 'positioning' => 'relative', + 'marginTop' => -1, + 'marginLeft' => 1, + 'width' => 80, + 'height' => 80, + 'wrappingStyle' => $wrappingStyle, + 'wrapDistanceRight' => Converter::cmToPoint(1), + 'wrapDistanceBottom' => Converter::cmToPoint(1), ) ); $section->addText($text); + printSeparator($section); } //Absolute positioning -$section->addTextBreak(3); $section->addText('Absolute positioning: see top right corner of page'); $section->addImage( 'resources/_mars.jpg', @@ -58,7 +70,7 @@ $section->addImage( ); //Relative positioning -$section->addTextBreak(3); +printSeparator($section); $section->addText('Relative positioning: Horizontal position center relative to column,'); $section->addText('Vertical position top relative to line'); $section->addImage( @@ -74,6 +86,14 @@ $section->addImage( ) ); +function printSeparator(Section $section) +{ + $section->addTextBreak(); + $lineStyle = array('weight' => 0.2, 'width' => 150, 'height' => 0, 'align' => 'center'); + $section->addLine($lineStyle); + $section->addTextBreak(2); +} + // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); if (!CLI) { diff --git a/samples/Sample_14_ListItem.php b/samples/Sample_14_ListItem.php index 689ac3d3..774fd284 100644 --- a/samples/Sample_14_ListItem.php +++ b/samples/Sample_14_ListItem.php @@ -67,6 +67,8 @@ $listItemRun->addText(' in bold', array('bold' => true)); $listItemRun = $section->addListItemRun(); $listItemRun->addText('List item 2'); $listItemRun->addText(' in italic', array('italic' => true)); +$footnote = $listItemRun->addFootnote(); +$footnote->addText('this is a footnote on a list item'); $listItemRun = $section->addListItemRun(); $listItemRun->addText('List item 3'); $listItemRun->addText(' underlined', array('underline' => 'dash')); diff --git a/samples/Sample_16_Object.php b/samples/Sample_16_Object.php index 8b05b9e8..c4db7f61 100644 --- a/samples/Sample_16_Object.php +++ b/samples/Sample_16_Object.php @@ -9,7 +9,7 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord(); $section = $phpWord->addSection(); $section->addText('You can open this OLE object by double clicking on the icon:'); $section->addTextBreak(2); -$section->addObject('resources/_sheet.xls'); +$section->addOLEObject('resources/_sheet.xls'); // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); diff --git a/samples/Sample_17_TitleTOC.php b/samples/Sample_17_TitleTOC.php index 306595eb..86e8e28c 100644 --- a/samples/Sample_17_TitleTOC.php +++ b/samples/Sample_17_TitleTOC.php @@ -4,6 +4,7 @@ include_once 'Sample_Header.php'; // New Word document echo date('H:i:s'), ' Create new PhpWord object', EOL; $phpWord = new \PhpOffice\PhpWord\PhpWord(); +$phpWord->getSettings()->setUpdateFields(true); // New section $section = $phpWord->addSection(); @@ -11,13 +12,14 @@ $section = $phpWord->addSection(); // Define styles $fontStyle12 = array('spaceAfter' => 60, 'size' => 12); $fontStyle10 = array('size' => 10); +$phpWord->addTitleStyle(null, array('size' => 22, 'bold' => true)); $phpWord->addTitleStyle(1, array('size' => 20, 'color' => '333333', 'bold' => true)); $phpWord->addTitleStyle(2, array('size' => 16, 'color' => '666666')); $phpWord->addTitleStyle(3, array('size' => 14, 'italic' => true)); $phpWord->addTitleStyle(4, array('size' => 12)); // Add text elements -$section->addText('Table of contents 1'); +$section->addTitle('Table of contents 1', 0); $section->addTextBreak(2); // Add TOC #1 diff --git a/samples/Sample_23_TemplateBlock.php b/samples/Sample_23_TemplateBlock.php index 2b7e9f68..ed986618 100644 --- a/samples/Sample_23_TemplateBlock.php +++ b/samples/Sample_23_TemplateBlock.php @@ -14,7 +14,7 @@ $templateProcessor->deleteBlock('DELETEME'); echo date('H:i:s'), ' Saving the result document...', EOL; $templateProcessor->saveAs('results/Sample_23_TemplateBlock.docx'); -echo getEndingNotes(array('Word2007' => 'docx')); +echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_23_TemplateBlock.docx'); if (!CLI) { include_once 'Sample_Footer.php'; } diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php index 4235c946..e1823c43 100644 --- a/samples/Sample_26_Html.php +++ b/samples/Sample_26_Html.php @@ -4,17 +4,92 @@ include_once 'Sample_Header.php'; // New Word Document echo date('H:i:s') , ' Create new PhpWord object' , EOL; $phpWord = new \PhpOffice\PhpWord\PhpWord(); +$phpWord->addParagraphStyle('Heading2', array('alignment' => 'center')); $section = $phpWord->addSection(); $html = '

Adding element via HTML

'; -$html .= '

Some well formed HTML snippet needs to be used

'; +$html .= '

Some well-formed HTML snippet needs to be used

'; $html .= '

With for example some1 inline formatting1

'; -$html .= '

Unordered (bulleted) list:

'; -$html .= '
  • Item 1
  • Item 2
    • Item 2.1
    • Item 2.1
'; -$html .= '

Ordered (numbered) list:

'; -$html .= '
  1. Item 1
  2. Item 2
'; -\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html); +$html .= '

A link to Read the docs

'; + +$html .= '

היי, זה פסקה מימין לשמאל

'; + +$html .= '

Unordered (bulleted) list:

'; +$html .= '
  • Item 1
  • Item 2
    • Item 2.1
    • Item 2.1
'; + +$html .= '

1.5 line height with first line text indent:

'; +$html .= '

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

'; + +$html .= '

centered title

'; + +$html .= '

Ordered (numbered) list:

'; +$html .= '
    +
  1. List 1 item 1

  2. +
  3. List 1 item 2
  4. +
      +
    1. sub list 1
    2. +
    3. sub list 2
    4. +
    +
  5. List 1 item 3
  6. +
+

A second list, numbering should restart

+
    +
  1. List 2 item 1
  2. +
  3. List 2 item 2
  4. +
  5. +
      +
    1. sub list 1
    2. +
    3. sub list 2
    4. +
    +
  6. +
  7. List 2 item 3
  8. +
      +
    1. sub list 1, restarts with a
    2. +
    3. sub list 2
    4. +
    +
'; + +$html .= '

List with formatted content:

'; +$html .= '
    +
  • + + big list item1 + +
  • +
  • + + list item2 in bold + +
  • +
'; + +$html .= '

A table with formatting:

'; +$html .= ' + + + + + + + + + + + +
header aheader bheader c
12
This is bold text6
'; + +$html .= '

Table inside another table:

'; +$html .= ' + + +
+ + +
column 1column 2
+
Cell in parent table
'; + +\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html, false, false); // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); diff --git a/samples/Sample_27_Field.php b/samples/Sample_27_Field.php index 57747895..4e7a5b22 100644 --- a/samples/Sample_27_Field.php +++ b/samples/Sample_27_Field.php @@ -1,28 +1,67 @@ 14)); // New section $section = $phpWord->addSection(); +$section->addTitle('This page demos fields'); // Add Field elements // See Element/Field.php for all options $section->addText('Date field:'); $section->addField('DATE', array('dateformat' => 'dddd d MMMM yyyy H:mm:ss'), array('PreserveFormat')); +$section->addText('Style Ref field:'); +$section->addField('STYLEREF', array('StyleIdentifier' => 'Heading 1')); + $section->addText('Page field:'); -$section->addField('PAGE', array('format' => 'ArabicDash')); +$section->addField('PAGE', array('format' => 'Arabic')); $section->addText('Number of pages field:'); -$section->addField('NUMPAGES', array('format' => 'Arabic', 'numformat' => '0,00'), array('PreserveFormat')); +$section->addField('NUMPAGES', array('numformat' => '0,00', 'format' => 'Arabic'), array('PreserveFormat')); +$section->addTextBreak(); + +$textrun = $section->addTextRun(); +$textrun->addText('An index field is '); +$textrun->addField('XE', array(), array('Italic'), 'My first index'); +$textrun->addText('here:'); + +$indexEntryText = new TextRun(); +$indexEntryText->addText('My '); +$indexEntryText->addText('bold index', array('bold' => true)); +$indexEntryText->addText(' entry'); + +$textrun = $section->addTextRun(); +$textrun->addText('A complex index field is '); +$textrun->addField('XE', array(), array('Bold'), $indexEntryText); +$textrun->addText('here:'); + +$section->addText('The actual index:'); +$section->addField('INDEX', array(), array('\\e " "'), 'right click to update the index'); $textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER)); $textrun->addText('This is the date of lunar calendar '); $textrun->addField('DATE', array('dateformat' => 'd-M-yyyy H:mm:ss'), array('PreserveFormat', 'LunarCalendar')); $textrun->addText(' written in a textrun.'); +$section->addTextBreak(); + +$macroText = new TextRun(); +$macroText->addText('Double click', array('bold' => true)); +$macroText->addText(' to '); +$macroText->addText('zoom to 100%', array('italic' => true)); + +$section->addText('A macro button with styled text:'); +$section->addField('MACROBUTTON', array('macroname' => 'Zoom100'), array(), $macroText); +$section->addTextBreak(); + +$section->addText('A macro button with simple text:'); +$section->addField('MACROBUTTON', array('macroname' => 'Zoom100'), array(), 'double click to zoom'); // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); diff --git a/samples/Sample_32_Chart.php b/samples/Sample_32_Chart.php index 2c6458ca..c24a6f8e 100644 --- a/samples/Sample_32_Chart.php +++ b/samples/Sample_32_Chart.php @@ -16,18 +16,23 @@ $section = $phpWord->addSection(); $section->addTitle('2D charts', 1); $section = $phpWord->addSection(array('colsNum' => 2, 'breakType' => 'continuous')); -$chartTypes = array('pie', 'doughnut', 'bar', 'column', 'line', 'area', 'scatter', 'radar'); -$twoSeries = array('bar', 'column', 'line', 'area', 'scatter', 'radar'); +$chartTypes = array('pie', 'doughnut', 'bar', 'column', 'line', 'area', 'scatter', 'radar', 'stacked_bar', 'percent_stacked_bar', 'stacked_column', 'percent_stacked_column'); +$twoSeries = array('bar', 'column', 'line', 'area', 'scatter', 'radar', 'stacked_bar', 'percent_stacked_bar', 'stacked_column', 'percent_stacked_column'); $threeSeries = array('bar', 'line'); $categories = array('A', 'B', 'C', 'D', 'E'); $series1 = array(1, 3, 2, 5, 4); $series2 = array(3, 1, 7, 2, 6); $series3 = array(8, 3, 2, 5, 4); +$showGridLines = false; +$showAxisLabels = false; foreach ($chartTypes as $chartType) { $section->addTitle(ucfirst($chartType), 2); $chart = $section->addChart($chartType, $categories, $series1); $chart->getStyle()->setWidth(Converter::inchToEmu(2.5))->setHeight(Converter::inchToEmu(2)); + $chart->getStyle()->setShowGridX($showGridLines); + $chart->getStyle()->setShowGridY($showGridLines); + $chart->getStyle()->setShowAxisLabels($showAxisLabels); if (in_array($chartType, $twoSeries)) { $chart->addSeries($categories, $series2); } @@ -44,7 +49,14 @@ $section = $phpWord->addSection(array('colsNum' => 2, 'breakType' => 'continuous $chartTypes = array('pie', 'bar', 'column', 'line', 'area'); $multiSeries = array('bar', 'column', 'line', 'area'); -$style = array('width' => Converter::cmToEmu(5), 'height' => Converter::cmToEmu(4), '3d' => true); +$style = array( + 'width' => Converter::cmToEmu(5), + 'height' => Converter::cmToEmu(4), + '3d' => true, + 'showAxisLabels' => $showAxisLabels, + 'showGridX' => $showGridLines, + 'showGridY' => $showGridLines, +); foreach ($chartTypes as $chartType) { $section->addTitle(ucfirst($chartType), 2); $chart = $section->addChart($chartType, $categories, $series1, $style); diff --git a/samples/Sample_34_SDT.php b/samples/Sample_34_SDT.php index a5042279..f9077a1a 100644 --- a/samples/Sample_34_SDT.php +++ b/samples/Sample_34_SDT.php @@ -15,10 +15,16 @@ $textrun->addSDT('comboBox')->setListItems(array('1' => 'Choice 1', '2' => 'Choi $textrun = $section->addTextRun(); $textrun->addText('Date: '); $textrun->addSDT('date'); +$textrun->addTextBreak(1); +$textrun->addText('Date with pre set value: '); +$textrun->addSDT('date')->setValue('03/30/2017'); +$textrun->addTextBreak(1); +$textrun->addText('Date with pre set value: '); +$textrun->addSDT('date')->setValue('30.03.2017'); $textrun = $section->addTextRun(); $textrun->addText('Drop down list: '); -$textrun->addSDT('dropDownList')->setListItems(array('1' => 'Choice 1', '2' => 'Choice 2')); +$textrun->addSDT('dropDownList')->setListItems(array('1' => 'Choice 1', '2' => 'Choice 2'))->setValue('Choice 1'); // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); diff --git a/samples/Sample_37_Comments.php b/samples/Sample_37_Comments.php new file mode 100644 index 00000000..268739bc --- /dev/null +++ b/samples/Sample_37_Comments.php @@ -0,0 +1,62 @@ +addText('Test', array('bold' => true)); +$phpWord->addComment($comment); + +$section = $phpWord->addSection(); + +$textrun = $section->addTextRun(); +$textrun->addText('This '); +$text = $textrun->addText('is'); +$text->setCommentRangeStart($comment); +$textrun->addText(' a test'); + +$section->addTextBreak(2); + +// Let's create a comment that we will link to a start element and an end element +$commentWithStartAndEnd = new \PhpOffice\PhpWord\Element\Comment('Foo Bar', new \DateTime()); +$commentWithStartAndEnd->addText('A comment with a start and an end'); +$phpWord->addComment($commentWithStartAndEnd); + +$textrunWithEnd = $section->addTextRun(); +$textrunWithEnd->addText('This '); +$textToStartOn = $textrunWithEnd->addText('is', array('bold' => true)); +$textToStartOn->setCommentRangeStart($commentWithStartAndEnd); +$textrunWithEnd->addText(' another', array('italic' => true)); +$textToEndOn = $textrunWithEnd->addText(' test'); +$textToEndOn->setCommentRangeEnd($commentWithStartAndEnd); + +$section->addTextBreak(2); + +// Let's add a comment on an image +$commentOnImage = new \PhpOffice\PhpWord\Element\Comment('Mr Smart', new \DateTime()); +$imageComment = $commentOnImage->addTextRun(); +$imageComment->addText('Hey, Mars does look '); +$imageComment->addText('red', array('color' => 'FF0000')); +$phpWord->addComment($commentOnImage); +$image = $section->addImage('resources/_mars.jpg'); +$image->setCommentRangeStart($commentOnImage); + +$section->addTextBreak(2); + +// We can also do things the other way round, link the comment to the element +$anotherText = $section->addText('another text'); + +$comment1 = new \PhpOffice\PhpWord\Element\Comment('Authors name', new \DateTime(), 'my_initials'); +$comment1->addText('Test', array('bold' => true)); +$comment1->setStartElement($anotherText); +$comment1->setEndElement($anotherText); +$phpWord->addComment($comment1); + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_38_Protection.php b/samples/Sample_38_Protection.php new file mode 100644 index 00000000..ee2b460b --- /dev/null +++ b/samples/Sample_38_Protection.php @@ -0,0 +1,21 @@ +getSettings()->getDocumentProtection(); +$documentProtection->setEditing(DocProtect::READ_ONLY); +$documentProtection->setPassword('myPassword'); + +$section = $phpWord->addSection(); +$section->addText('this document is password protected'); + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_39_TrackChanges.php b/samples/Sample_39_TrackChanges.php new file mode 100644 index 00000000..e6a30668 --- /dev/null +++ b/samples/Sample_39_TrackChanges.php @@ -0,0 +1,29 @@ +addSection(); +$textRun = $section->addTextRun(); + +$text = $textRun->addText('Hello World! Time to '); + +$text = $textRun->addText('wake ', array('bold' => true)); +$text->setChangeInfo(TrackChange::INSERTED, 'Fred', time() - 1800); + +$text = $textRun->addText('up'); +$text->setTrackChange(new TrackChange(TrackChange::INSERTED, 'Fred')); + +$text = $textRun->addText('go to sleep'); +$text->setChangeInfo(TrackChange::DELETED, 'Barney', new \DateTime('@' . (time() - 3600))); + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php index 90bdf4fa..f0fc6266 100644 --- a/samples/Sample_Header.php +++ b/samples/Sample_Header.php @@ -12,6 +12,12 @@ define('IS_INDEX', SCRIPT_FILENAME == 'index'); Settings::loadConfig(); +$dompdfPath = $vendorDirPath . '/dompdf/dompdf'; +if (file_exists($dompdfPath)) { + define('DOMPDF_ENABLE_AUTOLOAD', false); + Settings::setPdfRenderer(Settings::PDF_RENDERER_DOMPDF, $vendorDirPath . '/dompdf/dompdf'); +} + // Set writers $writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf'); @@ -37,13 +43,19 @@ $pageHeading = IS_INDEX ? '' : "

{$pageHeading}

"; // Populate samples $files = ''; if ($handle = opendir('.')) { - while (false !== ($file = readdir($handle))) { + $sampleFiles = array(); + while (false !== ($sampleFile = readdir($handle))) { + $sampleFiles[] = $sampleFile; + } + sort($sampleFiles); + closedir($handle); + + foreach ($sampleFiles as $file) { if (preg_match('/^Sample_\d+_/', $file)) { $name = str_replace('_', ' ', preg_replace('/(Sample_|\.php)/', '', $file)); $files .= "
  • {$name}
  • "; } } - closedir($handle); } /** @@ -71,7 +83,7 @@ function write($phpWord, $filename, $writers) $result .= EOL; } - $result .= getEndingNotes($writers); + $result .= getEndingNotes($writers, $filename); return $result; } @@ -80,17 +92,17 @@ function write($phpWord, $filename, $writers) * Get ending notes * * @param array $writers - * + * @param mixed $filename * @return string */ -function getEndingNotes($writers) +function getEndingNotes($writers, $filename) { $result = ''; // Do not show execution time for index if (!IS_INDEX) { - $result .= date('H:i:s') . " Done writing file(s)" . EOL; - $result .= date('H:i:s') . " Peak memory usage: " . (memory_get_peak_usage(true) / 1024 / 1024) . " MB" . EOL; + $result .= date('H:i:s') . ' Done writing file(s)' . EOL; + $result .= date('H:i:s') . ' Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . ' MB' . EOL; } // Return @@ -110,6 +122,12 @@ function getEndingNotes($writers) } } $result .= '

    '; + + $result .= '
    ';
    +            if (file_exists($filename . '.php')) {
    +                $result .= highlight_file($filename . '.php', true);
    +            }
    +            $result .= '
    '; } } diff --git a/samples/index.php b/samples/index.php index a65d8fd9..3dbc09ff 100644 --- a/samples/index.php +++ b/samples/index.php @@ -13,7 +13,7 @@ $requirements = array( 'xsl' => array('PHP extension XSL (optional)', extension_loaded('xsl')), ); if (!CLI) { -?> + ?>

    Welcome to PHPWord, a library written in pure PHP that provides a set of classes to write to and read from different document file formats, i.e. Office Open XML (.docx), Open Document Format (.odt), and Rich Text Format (.rtf).

     

    @@ -25,14 +25,14 @@ if (!CLI) { Requirement check:"; - echo "
      "; + echo '

      Requirement check:

      '; + echo '
        '; foreach ($requirements as $key => $value) { list($label, $result) = $value; $status = $result ? 'passed' : 'failed'; echo "
      • {$label} ... {$status}
      • "; } - echo "
      "; + echo '
    '; include_once 'Sample_Footer.php'; } else { echo 'Requirement check:' . PHP_EOL; diff --git a/samples/resources/Sample_11_ReadWord2007.docx b/samples/resources/Sample_11_ReadWord2007.docx index c9a50f48..406cf1e1 100644 Binary files a/samples/resources/Sample_11_ReadWord2007.docx and b/samples/resources/Sample_11_ReadWord2007.docx differ diff --git a/samples/resources/Sample_24_ReadODText.odt b/samples/resources/Sample_24_ReadODText.odt index d37c4e66..59ac16da 100644 Binary files a/samples/resources/Sample_24_ReadODText.odt and b/samples/resources/Sample_24_ReadODText.odt differ diff --git a/samples/resources/Sample_30_ReadHTML.html b/samples/resources/Sample_30_ReadHTML.html index 5593298b..b3d2fad2 100644 --- a/samples/resources/Sample_30_ReadHTML.html +++ b/samples/resources/Sample_30_ReadHTML.html @@ -11,5 +11,15 @@
    • Item 1
    • Item 2
      • Item 2.1
      • Item 2.1

    Ordered (numbered) list:

    1. Item 1
    2. Item 2
    + +

    Double height

    + +

    Includes images

    + + + + + + diff --git a/samples/results/.gitignore b/samples/results/.gitignore old mode 100644 new mode 100755 diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..7741cfb4 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,17 @@ +# must be unique in a given SonarQube instance +sonar.projectKey=phpoffice:phpword +# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. +sonar.projectName=PHPWord +sonar.projectVersion=0.16 + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +# This property is optional if sonar.modules is set. +sonar.sources=src +sonar.tests=tests +sonar.php.coverage.reportPaths=build/logs/clover.xml +sonar.php.tests.reportPath=build/logs/logfile.xml + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 + +sonar.host.url=http://localhost:9000 \ No newline at end of file diff --git a/src/PhpWord/Collection/AbstractCollection.php b/src/PhpWord/Collection/AbstractCollection.php index beff290e..899ec287 100644 --- a/src/PhpWord/Collection/AbstractCollection.php +++ b/src/PhpWord/Collection/AbstractCollection.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -27,14 +27,14 @@ abstract class AbstractCollection /** * Items * - * @var array + * @var \PhpOffice\PhpWord\Element\AbstractContainer[] */ private $items = array(); /** * Get items * - * @return array + * @return \PhpOffice\PhpWord\Element\AbstractContainer[] */ public function getItems() { @@ -45,23 +45,22 @@ abstract class AbstractCollection * Get item by index * * @param int $index - * @return mixed + * @return \PhpOffice\PhpWord\Element\AbstractContainer */ public function getItem($index) { if (array_key_exists($index, $this->items)) { return $this->items[$index]; - } else { - return null; } + + return null; } /** * Set item. * * @param int $index - * @param mixed $item - * @return void + * @param \PhpOffice\PhpWord\Element\AbstractContainer $item */ public function setItem($index, $item) { @@ -73,7 +72,7 @@ abstract class AbstractCollection /** * Add new item * - * @param mixed $item + * @param \PhpOffice\PhpWord\Element\AbstractContainer $item * @return int */ public function addItem($item) diff --git a/src/PhpWord/Collection/Bookmarks.php b/src/PhpWord/Collection/Bookmarks.php index b263cda7..b5ffd5f4 100644 --- a/src/PhpWord/Collection/Bookmarks.php +++ b/src/PhpWord/Collection/Bookmarks.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Collection/Charts.php b/src/PhpWord/Collection/Charts.php index 01f3f72e..aa807d1e 100644 --- a/src/PhpWord/Collection/Charts.php +++ b/src/PhpWord/Collection/Charts.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Collection/Comments.php b/src/PhpWord/Collection/Comments.php new file mode 100644 index 00000000..b6c02d39 --- /dev/null +++ b/src/PhpWord/Collection/Comments.php @@ -0,0 +1,27 @@ +pos; + } + + /** + * Set the Footnote Positioning Location (pageBottom, beneathText, sectEnd, docEnd) + * + * @param string $pos + * @throws \InvalidArgumentException + * @return self + */ + public function setPos($pos) + { + $position = array( + self::POSITION_PAGE_BOTTOM, + self::POSITION_BENEATH_TEXT, + self::POSITION_SECTION_END, + self::POSITION_DOC_END, + ); + + if (in_array($pos, $position)) { + $this->pos = $pos; + } else { + throw new \InvalidArgumentException('Invalid value, on of ' . implode(', ', $position) . ' possible'); + } + + return $this; + } + + /** + * Get the Footnote Numbering Format + * + * @return string + */ + public function getNumFmt() + { + return $this->numFmt; + } + + /** + * Set the Footnote Numbering Format + * + * @param string $numFmt One of NumberFormat + * @return self + */ + public function setNumFmt($numFmt) + { + NumberFormat::validate($numFmt); + $this->numFmt = $numFmt; + + return $this; + } + + /** + * Get the Footnote Numbering Format + * + * @return float + */ + public function getNumStart() + { + return $this->numStart; + } + + /** + * Set the Footnote Numbering Format + * + * @param float $numStart + * @return self + */ + public function setNumStart($numStart) + { + $this->numStart = $numStart; + + return $this; + } + + /** + * Get the Footnote and Endnote Numbering Starting Value + * + * @return string + */ + public function getNumRestart() + { + return $this->numRestart; + } + + /** + * Set the Footnote and Endnote Numbering Starting Value (continuous, eachSect, eachPage) + * + * @param string $numRestart + * @throws \InvalidArgumentException + * @return self + */ + public function setNumRestart($numRestart) + { + $restartNumbers = array( + self::RESTART_NUMBER_CONTINUOUS, + self::RESTART_NUMBER_EACH_SECTION, + self::RESTART_NUMBER_EACH_PAGE, + ); + + if (in_array($numRestart, $restartNumbers)) { + $this->numRestart = $numRestart; + } else { + throw new \InvalidArgumentException('Invalid value, on of ' . implode(', ', $restartNumbers) . ' possible'); + } + + return $this; + } +} diff --git a/src/PhpWord/ComplexType/ProofState.php b/src/PhpWord/ComplexType/ProofState.php new file mode 100644 index 00000000..4f8dafe3 --- /dev/null +++ b/src/PhpWord/ComplexType/ProofState.php @@ -0,0 +1,106 @@ +spelling = $spelling; + } else { + throw new \InvalidArgumentException('Invalid value, dirty or clean possible'); + } + + return $this; + } + + /** + * Get the Spell Checking State + * + * @return string + */ + public function getSpelling() + { + return $this->spelling; + } + + /** + * Set the Grammatical Checking State (dirty or clean) + * + * @param string $grammar + * @throws \InvalidArgumentException + * @return self + */ + public function setGrammar($grammar) + { + if ($grammar == self::CLEAN || $grammar == self::DIRTY) { + $this->grammar = $grammar; + } else { + throw new \InvalidArgumentException('Invalid value, dirty or clean possible'); + } + + return $this; + } + + /** + * Get the Grammatical Checking State + * + * @return string + */ + public function getGrammar() + { + return $this->grammar; + } +} diff --git a/src/PhpWord/ComplexType/TblWidth.php b/src/PhpWord/ComplexType/TblWidth.php new file mode 100644 index 00000000..0d1a2419 --- /dev/null +++ b/src/PhpWord/ComplexType/TblWidth.php @@ -0,0 +1,59 @@ +value = $value; + TblWidthSimpleType::validate($type); + $this->type = $type; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return int + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/PhpWord/ComplexType/TrackChangesView.php b/src/PhpWord/ComplexType/TrackChangesView.php new file mode 100644 index 00000000..92ea05ea --- /dev/null +++ b/src/PhpWord/ComplexType/TrackChangesView.php @@ -0,0 +1,166 @@ +markup; + } + + /** + * Set Display Visual Indicator Of Markup Area + * + * @param bool $markup + * Set to true to show markup + */ + public function setMarkup($markup) + { + $this->markup = $markup === null ? true : $markup; + } + + /** + * Get Display Comments + * + * @return bool True if comments are shown + */ + public function hasComments() + { + return $this->comments; + } + + /** + * Set Display Comments + * + * @param bool $comments + * Set to true to show comments + */ + public function setComments($comments) + { + $this->comments = $comments === null ? true : $comments; + } + + /** + * Get Display Content Revisions + * + * @return bool True if content revisions are shown + */ + public function hasInsDel() + { + return $this->insDel; + } + + /** + * Set Display Content Revisions + * + * @param bool $insDel + * Set to true to show content revisions + */ + public function setInsDel($insDel) + { + $this->insDel = $insDel === null ? true : $insDel; + } + + /** + * Get Display Formatting Revisions + * + * @return bool True if formatting revisions are shown + */ + public function hasFormatting() + { + return $this->formatting; + } + + /** + * Set Display Formatting Revisions + * + * @param bool|null $formatting + * Set to true to show formatting revisions + */ + public function setFormatting($formatting = null) + { + $this->formatting = $formatting === null ? true : $formatting; + } + + /** + * Get Display Ink Annotations + * + * @return bool True if ink annotations are shown + */ + public function hasInkAnnotations() + { + return $this->inkAnnotations; + } + + /** + * Set Display Ink Annotations + * + * @param bool $inkAnnotations + * Set to true to show ink annotations + */ + public function setInkAnnotations($inkAnnotations) + { + $this->inkAnnotations = $inkAnnotations === null ? true : $inkAnnotations; + } +} diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php index a3872e0b..204d4a73 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @method Text addText(string $text, mixed $fStyle = null, mixed $pStyle = null) * @method TextRun addTextRun(mixed $pStyle = null) * @method Bookmark addBookmark(string $name) - * @method Link addLink(string $target, string $text = null, mixed $fStyle = null, mixed $pStyle = null) + * @method Link addLink(string $target, string $text = null, mixed $fStyle = null, mixed $pStyle = null, boolean $internal = false) * @method PreserveText addPreserveText(string $text, mixed $fStyle = null, mixed $pStyle = null) * @method void addTextBreak(int $count = 1, mixed $fStyle = null, mixed $pStyle = null) * @method ListItem addListItem(string $txt, int $depth = 0, mixed $font = null, mixed $list = null, mixed $para = null) @@ -33,19 +33,20 @@ namespace PhpOffice\PhpWord\Element; * @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null) * @method Title addTitle(string $text, int $depth = 1) * @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9) - * * @method PageBreak addPageBreak() * @method Table addTable(mixed $style = null) - * @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false) - * @method Object addObject(string $source, mixed $style = null) + * @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false, $name = null) + * @method OLEObject addOLEObject(string $source, mixed $style = null) * @method TextBox addTextBox(mixed $style = null) - * @method Field addField(string $type = null, array $properties = array(), array $options = array()) + * @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null) * @method Line addLine(mixed $lineStyle = null) * @method Shape addShape(string $type, mixed $style = null) * @method Chart addChart(string $type, array $categories, array $values, array $style = null) * @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null) * @method SDT addSDT(string $type) * + * @method \PhpOffice\PhpWord\Element\OLEObject addObject(string $source, mixed $style = null) deprecated, use addOLEObject instead + * * @since 0.10.0 */ abstract class AbstractContainer extends AbstractElement @@ -53,12 +54,12 @@ abstract class AbstractContainer extends AbstractElement /** * Elements collection * - * @var array + * @var \PhpOffice\PhpWord\Element\AbstractElement[] */ protected $elements = array(); /** - * Container type Section|Header|Footer|Footnote|Endnote|Cell|TextRun|TextBox|ListItemRun + * Container type Section|Header|Footer|Footnote|Endnote|Cell|TextRun|TextBox|ListItemRun|TrackChange * * @var string */ @@ -80,14 +81,14 @@ abstract class AbstractContainer extends AbstractElement { $elements = array( 'Text', 'TextRun', 'Bookmark', 'Link', 'PreserveText', 'TextBreak', - 'ListItem', 'ListItemRun', 'Table', 'Image', 'Object', + 'ListItem', 'ListItemRun', 'Table', 'Image', 'Object', 'OLEObject', 'Footnote', 'Endnote', 'CheckBox', 'TextBox', 'Field', 'Line', 'Shape', 'Title', 'TOC', 'PageBreak', - 'Chart', 'FormField', 'SDT' + 'Chart', 'FormField', 'SDT', 'Comment', ); $functions = array(); foreach ($elements as $element) { - $functions['add' . strtolower($element)] = $element; + $functions['add' . strtolower($element)] = $element == 'Object' ? 'OLEObject' : $element; } // Run valid `add` command @@ -98,16 +99,15 @@ abstract class AbstractContainer extends AbstractElement // Special case for TextBreak // @todo Remove the `$count` parameter in 1.0.0 to make this element similiar to other elements? if ($element == 'TextBreak') { - @list($count, $fontStyle, $paragraphStyle) = $args; // Suppress error + list($count, $fontStyle, $paragraphStyle) = array_pad($args, 3, null); if ($count === null) { $count = 1; } for ($i = 1; $i <= $count; $i++) { $this->addElement($element, $fontStyle, $paragraphStyle); } - - // All other elements } else { + // All other elements array_unshift($args, $element); // Prepend element name to the beginning of args array return call_user_func_array(array($this, 'addElement'), $args); } @@ -157,15 +157,48 @@ abstract class AbstractContainer extends AbstractElement /** * Get all elements * - * @return array - * - * @codeCoverageIgnore + * @return \PhpOffice\PhpWord\Element\AbstractElement[] */ public function getElements() { return $this->elements; } + /** + * Returns the element at the requested position + * + * @param int $index + * @return \PhpOffice\PhpWord\Element\AbstractElement|null + */ + public function getElement($index) + { + if (array_key_exists($index, $this->elements)) { + return $this->elements[$index]; + } + + return null; + } + + /** + * Removes the element at requested index + * + * @param int|\PhpOffice\PhpWord\Element\AbstractElement $toRemove + */ + public function removeElement($toRemove) + { + if (is_int($toRemove) && array_key_exists($toRemove, $this->elements)) { + unset($this->elements[$toRemove]); + } elseif ($toRemove instanceof \PhpOffice\PhpWord\Element\AbstractElement) { + foreach ($this->elements as $key => $element) { + if ($element->getElementId() === $toRemove->getElementId()) { + unset($this->elements[$key]); + + return; + } + } + } + } + /** * Count elements * @@ -181,14 +214,13 @@ abstract class AbstractContainer extends AbstractElement * * @param string $method * - * @return bool - * * @throws \BadMethodCallException + * @return bool */ private function checkValidity($method) { $generalContainers = array( - 'Section', 'Header', 'Footer', 'Footnote', 'Endnote', 'Cell', 'TextRun', 'TextBox', 'ListItemRun', + 'Section', 'Header', 'Footer', 'Footnote', 'Endnote', 'Cell', 'TextRun', 'TextBox', 'ListItemRun', 'TrackChange', ); $validContainers = array( @@ -197,25 +229,26 @@ abstract class AbstractContainer extends AbstractElement 'Link' => $generalContainers, 'TextBreak' => $generalContainers, 'Image' => $generalContainers, - 'Object' => $generalContainers, + 'OLEObject' => $generalContainers, 'Field' => $generalContainers, 'Line' => $generalContainers, 'Shape' => $generalContainers, 'FormField' => $generalContainers, 'SDT' => $generalContainers, - 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), + 'TrackChange' => $generalContainers, + 'TextRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox', 'TrackChange', 'ListItemRun'), 'ListItem' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), 'ListItemRun' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), 'Table' => array('Section', 'Header', 'Footer', 'Cell', 'TextBox'), - 'CheckBox' => array('Section', 'Header', 'Footer', 'Cell'), + 'CheckBox' => array('Section', 'Header', 'Footer', 'Cell', 'TextRun'), 'TextBox' => array('Section', 'Header', 'Footer', 'Cell'), - 'Footnote' => array('Section', 'TextRun', 'Cell'), + 'Footnote' => array('Section', 'TextRun', 'Cell', 'ListItemRun'), 'Endnote' => array('Section', 'TextRun', 'Cell'), - 'PreserveText' => array('Header', 'Footer', 'Cell'), - 'Title' => array('Section'), + 'PreserveText' => array('Section', 'Header', 'Footer', 'Cell'), + 'Title' => array('Section', 'Cell'), 'TOC' => array('Section'), 'PageBreak' => array('Section'), - 'Chart' => array('Section'), + 'Chart' => array('Section', 'Cell'), ); // Special condition, e.g. preservetext can only exists in cell when diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php index b0ed8ae2..e3e54ed4 100644 --- a/src/PhpWord/Element/AbstractElement.php +++ b/src/PhpWord/Element/AbstractElement.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -73,7 +73,7 @@ abstract class AbstractElement /** * Unique Id for element * - * @var int + * @var string */ protected $elementId; @@ -93,6 +93,20 @@ abstract class AbstractElement */ private $nestedLevel = 0; + /** + * A reference to the parent + * + * @var \PhpOffice\PhpWord\Element\AbstractElement + */ + private $parent; + + /** + * changed element info + * + * @var TrackChange + */ + private $trackChange; + /** * Parent container type * @@ -108,12 +122,26 @@ abstract class AbstractElement protected $mediaRelation = false; /** - * Is part of collection; true for Title, Footnote, Endnote, and Chart + * Is part of collection; true for Title, Footnote, Endnote, Chart, and Comment * * @var bool */ protected $collectionRelation = false; + /** + * The start position for the linked comment + * + * @var Comment + */ + protected $commentRangeStart; + + /** + * The end position for the linked comment + * + * @var Comment + */ + protected $commentRangeEnd; + /** * Get PhpWord * @@ -128,7 +156,6 @@ abstract class AbstractElement * Set PhpWord as reference. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function setPhpWord(PhpWord $phpWord = null) { @@ -150,7 +177,6 @@ abstract class AbstractElement * * @param string $docPart * @param int $docPartId - * @return void */ public function setDocPart($docPart, $docPartId = 1) { @@ -207,7 +233,6 @@ abstract class AbstractElement * Set element index. * * @param int $value - * @return void */ public function setElementIndex($value) { @@ -226,8 +251,6 @@ abstract class AbstractElement /** * Set element unique ID from 6 first digit of md5. - * - * @return void */ public function setElementId() { @@ -248,7 +271,6 @@ abstract class AbstractElement * Set relation Id. * * @param int $value - * @return void */ public function setRelationId($value) { @@ -265,17 +287,70 @@ abstract class AbstractElement return $this->nestedLevel; } + /** + * Get comment start + * + * @return Comment + */ + public function getCommentRangeStart() + { + return $this->commentRangeStart; + } + + /** + * Set comment start + * + * @param Comment $value + */ + public function setCommentRangeStart(Comment $value) + { + if ($this instanceof Comment) { + throw new \InvalidArgumentException('Cannot set a Comment on a Comment'); + } + $this->commentRangeStart = $value; + $this->commentRangeStart->setStartElement($this); + } + + /** + * Get comment end + * + * @return Comment + */ + public function getCommentRangeEnd() + { + return $this->commentRangeEnd; + } + + /** + * Set comment end + * + * @param Comment $value + */ + public function setCommentRangeEnd(Comment $value) + { + if ($this instanceof Comment) { + throw new \InvalidArgumentException('Cannot set a Comment on a Comment'); + } + $this->commentRangeEnd = $value; + $this->commentRangeEnd->setEndElement($this); + } + + public function getParent() + { + return $this->parent; + } + /** * Set parent container * * Passed parameter should be a container, except for Table (contain Row) and Row (contain Cell) * * @param \PhpOffice\PhpWord\Element\AbstractElement $container - * @return void */ - public function setParentContainer(AbstractElement $container) + public function setParentContainer(self $container) { $this->parentContainer = substr(get_class($container), strrpos(get_class($container), '\\') + 1); + $this->parent = $container; // Set nested level $this->nestedLevel = $container->getNestedLevel(); @@ -300,16 +375,17 @@ abstract class AbstractElement * * - Image element needs to be passed to Media object * - Icon needs to be set for Object element - * - * @return void */ private function setMediaRelation() { - if (!$this instanceof Link && !$this instanceof Image && !$this instanceof Object) { + if (!$this instanceof Link && !$this instanceof Image && !$this instanceof OLEObject) { return; } $elementName = substr(get_class($this), strrpos(get_class($this), '\\') + 1); + if ($elementName == 'OLEObject') { + $elementName = 'Object'; + } $mediaPart = $this->getMediaPart(); $source = $this->getSource(); $image = null; @@ -319,7 +395,7 @@ abstract class AbstractElement $rId = Media::addElement($mediaPart, strtolower($elementName), $source, $image); $this->setRelationId($rId); - if ($this instanceof Object) { + if ($this instanceof OLEObject) { $icon = $this->getIcon(); $rId = Media::addElement($mediaPart, 'image', $icon, new Image($icon)); $this->setImageRelationId($rId); @@ -328,8 +404,6 @@ abstract class AbstractElement /** * Set relation Id for elements that will be registered in the Collection subnamespaces. - * - * @return void */ private function setCollectionRelation() { @@ -348,7 +422,7 @@ abstract class AbstractElement */ public function isInSection() { - return ($this->docPart == 'Section'); + return $this->docPart == 'Section'; } /** @@ -371,22 +445,53 @@ abstract class AbstractElement return $style; } + /** + * Sets the trackChange information + * + * @param TrackChange $trackChange + */ + public function setTrackChange(TrackChange $trackChange) + { + $this->trackChange = $trackChange; + } + + /** + * Gets the trackChange information + * + * @return TrackChange + */ + public function getTrackChange() + { + return $this->trackChange; + } + + /** + * Set changed + * + * @param string $type INSERTED|DELETED + * @param string $author + * @param null|int|\DateTime $date allways in UTC + */ + public function setChangeInfo($type, $author, $date = null) + { + $this->trackChange = new TrackChange($type, $author, $date); + } + /** * Set enum value * - * @param mixed $value - * @param array $enum - * @param mixed $default - * - * @return mixed + * @param string|null $value + * @param string[] $enum + * @param string|null $default * * @throws \InvalidArgumentException + * @return string|null * * @todo Merge with the same method in AbstractStyle */ protected function setEnumVal($value = null, $enum = array(), $default = null) { - if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) { + if ($value !== null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) { throw new \InvalidArgumentException("Invalid style value: {$value}"); } elseif ($value === null || trim($value) == '') { $value = $default; diff --git a/src/PhpWord/Element/Bookmark.php b/src/PhpWord/Element/Bookmark.php index c865893f..16b020d7 100644 --- a/src/PhpWord/Element/Bookmark.php +++ b/src/PhpWord/Element/Bookmark.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -43,11 +43,9 @@ class Bookmark extends AbstractElement * * @param string $name */ - public function __construct($name) + public function __construct($name = '') { - $this->name = CommonText::toUTF8($name); - return $this; } /** diff --git a/src/PhpWord/Element/Cell.php b/src/PhpWord/Element/Cell.php index 28e517fd..68f5df62 100644 --- a/src/PhpWord/Element/Cell.php +++ b/src/PhpWord/Element/Cell.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/Chart.php b/src/PhpWord/Element/Chart.php index f98c1d74..92152c87 100644 --- a/src/PhpWord/Element/Chart.php +++ b/src/PhpWord/Element/Chart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -61,11 +61,12 @@ class Chart extends AbstractElement * @param array $categories * @param array $values * @param array $style + * @param null|mixed $seriesName */ - public function __construct($type, $categories, $values, $style = null) + public function __construct($type, $categories, $values, $style = null, $seriesName = null) { $this->setType($type); - $this->addSeries($categories, $values); + $this->addSeries($categories, $values, $seriesName); $this->style = $this->setNewStyle(new ChartStyle(), $style, true); } @@ -83,11 +84,10 @@ class Chart extends AbstractElement * Set type. * * @param string $value - * @return void */ public function setType($value) { - $enum = array('pie', 'doughnut', 'line', 'bar', 'column', 'area', 'radar', 'scatter'); + $enum = array('pie', 'doughnut', 'line', 'bar', 'stacked_bar', 'percent_stacked_bar', 'column', 'stacked_column', 'percent_stacked_column', 'area', 'radar', 'scatter'); $this->type = $this->setEnumVal($value, $enum, 'pie'); } @@ -96,11 +96,15 @@ class Chart extends AbstractElement * * @param array $categories * @param array $values - * @return void + * @param null|mixed $name */ - public function addSeries($categories, $values) + public function addSeries($categories, $values, $name = null) { - $this->series[] = array('categories' => $categories, 'values' => $values); + $this->series[] = array( + 'categories' => $categories, + 'values' => $values, + 'name' => $name, + ); } /** diff --git a/src/PhpWord/Element/CheckBox.php b/src/PhpWord/Element/CheckBox.php index 3fc578ef..f3e87176 100644 --- a/src/PhpWord/Element/CheckBox.php +++ b/src/PhpWord/Element/CheckBox.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -40,7 +40,6 @@ class CheckBox extends Text * @param string $text * @param mixed $fontStyle * @param mixed $paragraphStyle - * @return self */ public function __construct($name = null, $text = null, $fontStyle = null, $paragraphStyle = null) { diff --git a/src/PhpWord/Element/Comment.php b/src/PhpWord/Element/Comment.php new file mode 100644 index 00000000..96ad15ef --- /dev/null +++ b/src/PhpWord/Element/Comment.php @@ -0,0 +1,122 @@ +initials = $initials; + } + + /** + * Get Initials + * + * @return string + */ + public function getInitials() + { + return $this->initials; + } + + /** + * Sets the element where this comment starts + * + * @param \PhpOffice\PhpWord\Element\AbstractElement $value + */ + public function setStartElement(AbstractElement $value) + { + $this->startElement = $value; + if ($value->getCommentRangeStart() == null) { + $value->setCommentRangeStart($this); + } + } + + /** + * Get the element where this comment starts + * + * @return \PhpOffice\PhpWord\Element\AbstractElement + */ + public function getStartElement() + { + return $this->startElement; + } + + /** + * Sets the element where this comment ends + * + * @param \PhpOffice\PhpWord\Element\AbstractElement $value + */ + public function setEndElement(AbstractElement $value) + { + $this->endElement = $value; + if ($value->getCommentRangeEnd() == null) { + $value->setCommentRangeEnd($this); + } + } + + /** + * Get the element where this comment ends + * + * @return \PhpOffice\PhpWord\Element\AbstractElement + */ + public function getEndElement() + { + return $this->endElement; + } +} diff --git a/src/PhpWord/Element/Endnote.php b/src/PhpWord/Element/Endnote.php index 2d8e4731..b9627195 100644 --- a/src/PhpWord/Element/Endnote.php +++ b/src/PhpWord/Element/Endnote.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Element; -use PhpOffice\PhpWord\Style\Paragraph; - /** * Endnote element * @@ -38,6 +36,6 @@ class Endnote extends Footnote */ public function __construct($paragraphStyle = null) { - $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); + parent::__construct($paragraphStyle); } } diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index 48dc1d2e..2efc6b0b 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,7 +21,7 @@ namespace PhpOffice\PhpWord\Element; * Field element * * @since 0.11.0 - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_SimpleField.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_SimpleField.html */ class Field extends AbstractElement { @@ -32,27 +32,56 @@ class Field extends AbstractElement * @var array */ protected $fieldsArray = array( - 'PAGE'=>array( - 'properties'=>array( + 'PAGE' => array( + 'properties' => array( 'format' => array('Arabic', 'ArabicDash', 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN'), ), - 'options'=>array('PreserveFormat') + 'options' => array('PreserveFormat'), ), - 'NUMPAGES'=>array( - 'properties'=>array( - 'format' => array('Arabic', 'ArabicDash', 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN'), - 'numformat' => array('0', '0,00', '#.##0', '#.##0,00', '€ #.##0,00(€ #.##0,00)', '0%', '0,00%') + 'NUMPAGES' => array( + 'properties' => array( + 'format' => array('Arabic', 'ArabicDash', 'CardText', 'DollarText', 'Ordinal', 'OrdText', + 'alphabetic', 'ALPHABETIC', 'roman', 'ROMAN', 'Caps', 'FirstCap', 'Lower', 'Upper', ), + 'numformat' => array('0', '0,00', '#.##0', '#.##0,00', '€ #.##0,00(€ #.##0,00)', '0%', '0,00%'), ), - 'options'=>array('PreserveFormat') + 'options' => array('PreserveFormat'), ), - 'DATE'=>array( - 'properties'=> array( - 'dateformat' =>array('d-M-yyyy', 'dddd d MMMM yyyy', 'd MMMM yyyy', 'd-M-yy', 'yyyy-MM-dd', - 'd-MMM-yy', 'd/M/yyyy', 'd MMM. yy', 'd/M/yy', 'MMM-yy', 'd-M-yyy H:mm', 'd-M-yyyy H:mm:ss', - 'h:mm am/pm', 'h:mm:ss am/pm', 'HH:mm', 'HH:mm:ss') + 'DATE' => array( + 'properties' => array( + 'dateformat' => array( + /* Generic formats */ + 'yyyy-MM-dd', 'yyyy-MM', 'MMM-yy', 'MMM-yyyy', 'h:mm am/pm', 'h:mm:ss am/pm', 'HH:mm', 'HH:mm:ss', + /* Day-Month-Year formats */ + 'dddd d MMMM yyyy', 'd MMMM yyyy', 'd-MMM-yy', 'd MMM. yy', + 'd-M-yy', 'd-M-yy h:mm', 'd-M-yy h:mm:ss', 'd-M-yy h:mm am/pm', 'd-M-yy h:mm:ss am/pm', 'd-M-yy HH:mm', 'd-M-yy HH:mm:ss', + 'd/M/yy', 'd/M/yy h:mm', 'd/M/yy h:mm:ss', 'd/M/yy h:mm am/pm', 'd/M/yy h:mm:ss am/pm', 'd/M/yy HH:mm', 'd/M/yy HH:mm:ss', + 'd-M-yyyy', 'd-M-yyyy h:mm', 'd-M-yyyy h:mm:ss', 'd-M-yyyy h:mm am/pm', 'd-M-yyyy h:mm:ss am/pm', 'd-M-yyyy HH:mm', 'd-M-yyyy HH:mm:ss', + 'd/M/yyyy', 'd/M/yyyy h:mm', 'd/M/yyyy h:mm:ss', 'd/M/yyyy h:mm am/pm', 'd/M/yyyy h:mm:ss am/pm', 'd/M/yyyy HH:mm', 'd/M/yyyy HH:mm:ss', + /* Month-Day-Year formats */ + 'dddd, MMMM d yyyy', 'MMMM d yyyy', 'MMM-d-yy', 'MMM. d yy', + 'M-d-yy', 'M-d-yy h:mm', 'M-d-yy h:mm:ss', 'M-d-yy h:mm am/pm', 'M-d-yy h:mm:ss am/pm', 'M-d-yy HH:mm', 'M-d-yy HH:mm:ss', + 'M/d/yy', 'M/d/yy h:mm', 'M/d/yy h:mm:ss', 'M/d/yy h:mm am/pm', 'M/d/yy h:mm:ss am/pm', 'M/d/yy HH:mm', 'M/d/yy HH:mm:ss', + 'M-d-yyyy', 'M-d-yyyy h:mm', 'M-d-yyyy h:mm:ss', 'M-d-yyyy h:mm am/pm', 'M-d-yyyy h:mm:ss am/pm', 'M-d-yyyy HH:mm', 'M-d-yyyy HH:mm:ss', + 'M/d/yyyy', 'M/d/yyyy h:mm', 'M/d/yyyy h:mm:ss', 'M/d/yyyy h:mm am/pm', 'M/d/yyyy h:mm:ss am/pm', 'M/d/yyyy HH:mm', 'M/d/yyyy HH:mm:ss', + ), ), - 'options'=>array('PreserveFormat', 'LunarCalendar', 'SakaEraCalendar', 'LastUsedFormat') - ) + 'options' => array('PreserveFormat', 'LunarCalendar', 'SakaEraCalendar', 'LastUsedFormat'), + ), + 'MACROBUTTON' => array( + 'properties' => array('macroname' => ''), + ), + 'XE' => array( + 'properties' => array(), + 'options' => array('Bold', 'Italic'), + ), + 'INDEX' => array( + 'properties' => array(), + 'options' => array('PreserveFormat'), + ), + 'STYLEREF' => array( + 'properties' => array('StyleIdentifier' => ''), + 'options' => array('PreserveFormat'), + ), ); /** @@ -62,6 +91,13 @@ class Field extends AbstractElement */ protected $type; + /** + * Field text + * + * @var TextRun|string + */ + protected $text; + /** * Field properties * @@ -76,18 +112,27 @@ class Field extends AbstractElement */ protected $options = array(); + /** + * Font style + * + * @var \PhpOffice\PhpWord\Style\Font + */ + protected $fontStyle; + /** * Create a new Field Element * * @param string $type * @param array $properties * @param array $options + * @param TextRun|string|null $text */ - public function __construct($type = null, $properties = array(), $options = array()) + public function __construct($type = null, $properties = array(), $options = array(), $text = null) { $this->setType($type); $this->setProperties($properties); $this->setOptions($options); + $this->setText($text); } /** @@ -95,9 +140,8 @@ class Field extends AbstractElement * * @param string $type * - * @return string - * * @throws \InvalidArgumentException + * @return string */ public function setType($type = null) { @@ -105,9 +149,10 @@ class Field extends AbstractElement if (isset($this->fieldsArray[$type])) { $this->type = $type; } else { - throw new \InvalidArgumentException("Invalid type"); + throw new \InvalidArgumentException("Invalid type '$type'"); } } + return $this->type; } @@ -126,20 +171,20 @@ class Field extends AbstractElement * * @param array $properties * - * @return self - * * @throws \InvalidArgumentException + * @return self */ public function setProperties($properties = array()) { if (is_array($properties)) { foreach (array_keys($properties) as $propkey) { if (!(isset($this->fieldsArray[$this->type]['properties'][$propkey]))) { - throw new \InvalidArgumentException("Invalid property"); + throw new \InvalidArgumentException("Invalid property '$propkey'"); } } $this->properties = array_merge($this->properties, $properties); } + return $this->properties; } @@ -158,20 +203,20 @@ class Field extends AbstractElement * * @param array $options * - * @return self - * * @throws \InvalidArgumentException + * @return self */ public function setOptions($options = array()) { if (is_array($options)) { foreach (array_keys($options) as $optionkey) { - if (!(isset($this->fieldsArray[$this->type]['options'][$optionkey]))) { - throw new \InvalidArgumentException("Invalid option"); + if (!(isset($this->fieldsArray[$this->type]['options'][$optionkey])) && substr($optionkey, 0, 1) !== '\\') { + throw new \InvalidArgumentException("Invalid option '$optionkey', possible values are " . implode(', ', $this->fieldsArray[$this->type]['options'])); } } $this->options = array_merge($this->options, $options); } + return $this->options; } @@ -184,4 +229,35 @@ class Field extends AbstractElement { return $this->options; } + + /** + * Set Field text + * + * @param string|TextRun $text + * + * @throws \InvalidArgumentException + * @return null|string|TextRun + */ + public function setText($text = null) + { + if (isset($text)) { + if (is_string($text) || $text instanceof TextRun) { + $this->text = $text; + } else { + throw new \InvalidArgumentException('Invalid text'); + } + } + + return $this->text; + } + + /** + * Get Field text + * + * @return string|TextRun + */ + public function getText() + { + return $this->text; + } } diff --git a/src/PhpWord/Element/Footer.php b/src/PhpWord/Element/Footer.php index 01c6d25c..0290d7c1 100644 --- a/src/PhpWord/Element/Footer.php +++ b/src/PhpWord/Element/Footer.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,11 +26,11 @@ class Footer extends AbstractContainer * Header/footer types constants * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-wtype-4.html Header or Footer Type + * @see http://www.datypic.com/sc/ooxml/t-w_ST_HdrFtr.html Header or Footer Type */ - const AUTO = 'default'; // default and odd pages + const AUTO = 'default'; // default and odd pages const FIRST = 'first'; - const EVEN = 'even'; + const EVEN = 'even'; /** * @var string Container type @@ -64,7 +64,6 @@ class Footer extends AbstractContainer * @since 0.10.0 * * @param string $value - * @return void */ public function setType($value = self::AUTO) { diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php index 73350bb7..90aabccc 100644 --- a/src/PhpWord/Element/Footnote.php +++ b/src/PhpWord/Element/Footnote.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,9 +19,6 @@ namespace PhpOffice\PhpWord\Element; use PhpOffice\PhpWord\Style\Paragraph; -/** - * @codeCoverageIgnore - */ class Footnote extends AbstractContainer { /** @@ -68,6 +65,7 @@ class Footnote extends AbstractContainer * Get Footnote Reference ID * * @deprecated 0.10.0 + * @codeCoverageIgnore * * @return int */ @@ -80,6 +78,7 @@ class Footnote extends AbstractContainer * Set Footnote Reference ID * * @deprecated 0.10.0 + * @codeCoverageIgnore * * @param int $rId */ diff --git a/src/PhpWord/Element/FormField.php b/src/PhpWord/Element/FormField.php index c7cb44d2..f937df59 100644 --- a/src/PhpWord/Element/FormField.php +++ b/src/PhpWord/Element/FormField.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,7 +21,7 @@ namespace PhpOffice\PhpWord\Element; * Form field element * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFData.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFData.html */ class FormField extends Text { @@ -35,7 +35,7 @@ class FormField extends Text /** * Form field name * - * @var string + * @var string|bool|int */ private $name; @@ -70,10 +70,10 @@ class FormField extends Text * @param string $type * @param mixed $fontStyle * @param mixed $paragraphStyle - * @return self */ public function __construct($type, $fontStyle = null, $paragraphStyle = null) { + parent::__construct(null, $fontStyle, $paragraphStyle); $this->setType($type); } diff --git a/src/PhpWord/Element/Header.php b/src/PhpWord/Element/Header.php index d4afdb86..8a01946e 100644 --- a/src/PhpWord/Element/Header.php +++ b/src/PhpWord/Element/Header.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index d7e5a665..bae87ff5 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -61,10 +61,17 @@ class Image extends AbstractElement /** * Is watermark * - * @var boolean + * @var bool */ private $watermark; + /** + * Name of image + * + * @var string + */ + private $name; + /** * Image type * @@ -96,7 +103,7 @@ class Image extends AbstractElement /** * Is memory image * - * @var boolean + * @var bool */ private $memoryImage; @@ -110,7 +117,7 @@ class Image extends AbstractElement /** * Image media index * - * @var integer + * @var int */ private $mediaIndex; @@ -126,18 +133,20 @@ class Image extends AbstractElement * * @param string $source * @param mixed $style - * @param boolean $watermark + * @param bool $watermark + * @param string $name * * @throws \PhpOffice\PhpWord\Exception\InvalidImageException * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException */ - public function __construct($source, $style = null, $watermark = false) + public function __construct($source, $style = null, $watermark = false, $name = null) { $this->source = $source; - $this->setIsWatermark($watermark); $this->style = $this->setNewStyle(new ImageStyle(), $style, true); + $this->setIsWatermark($watermark); + $this->setName($name); - $this->checkImage($source); + $this->checkImage(); } /** @@ -170,6 +179,26 @@ class Image extends AbstractElement return $this->sourceType; } + /** + * Sets the image name + * + * @param string $value + */ + public function setName($value) + { + $this->name = $value; + } + + /** + * Get image name + * + * @return null|string + */ + public function getName() + { + return $this->name; + } + /** * Get image media ID * @@ -183,7 +212,7 @@ class Image extends AbstractElement /** * Get is watermark * - * @return boolean + * @return bool */ public function isWatermark() { @@ -193,7 +222,7 @@ class Image extends AbstractElement /** * Set is watermark * - * @param boolean $value + * @param bool $value */ public function setIsWatermark($value) { @@ -243,7 +272,7 @@ class Image extends AbstractElement /** * Get is memory image * - * @return boolean + * @return bool */ public function isMemImage() { @@ -264,7 +293,6 @@ class Image extends AbstractElement * Set target file name. * * @param string $value - * @return void */ public function setTarget($value) { @@ -274,7 +302,7 @@ class Image extends AbstractElement /** * Get media index * - * @return integer + * @return int */ public function getMediaIndex() { @@ -284,8 +312,7 @@ class Image extends AbstractElement /** * Set media index. * - * @param integer $value - * @return void + * @param int $value */ public function setMediaIndex($value) { @@ -315,7 +342,7 @@ class Image extends AbstractElement $zip = new ZipArchive(); if ($zip->open($zipFilename) !== false) { - if ($zip->locateName($imageFilename)) { + if ($zip->locateName($imageFilename) !== false) { $isTemp = true; $zip->extractTo(Settings::getTempDir(), $imageFilename); $actualSource = Settings::getTempDir() . DIRECTORY_SEPARATOR . $imageFilename; @@ -336,10 +363,16 @@ class Image extends AbstractElement // Read image binary data and convert to hex/base64 string if ($this->sourceType == self::SOURCE_GD) { $imageResource = call_user_func($this->imageCreateFunc, $actualSource); + if ($this->imageType === 'image/png') { + // PNG images need to preserve alpha channel information + imagesavealpha($imageResource, true); + } ob_start(); call_user_func($this->imageFunc, $imageResource); $imageBinary = ob_get_contents(); ob_end_clean(); + } elseif ($this->sourceType == self::SOURCE_STRING) { + $imageBinary = $this->source; } else { $fileHandle = fopen($actualSource, 'rb', false); if ($fileHandle !== false) { @@ -366,33 +399,29 @@ class Image extends AbstractElement /** * Check memory image, supported type, image functions, and proportional width/height. * - * @param string $source - * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\InvalidImageException * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException */ - private function checkImage($source) + private function checkImage() { - $this->setSourceType($source); + $this->setSourceType(); // Check image data if ($this->sourceType == self::SOURCE_ARCHIVE) { - $imageData = $this->getArchiveImageSize($source); - } else if ($this->sourceType == self::SOURCE_STRING) { - $imageData = $this->getStringImageSize($source); + $imageData = $this->getArchiveImageSize($this->source); + } elseif ($this->sourceType == self::SOURCE_STRING) { + $imageData = $this->getStringImageSize($this->source); } else { - $imageData = @getimagesize($source); + $imageData = @getimagesize($this->source); } if (!is_array($imageData)) { - throw new InvalidImageException(sprintf('Invalid image: %s', $source)); + throw new InvalidImageException(sprintf('Invalid image: %s', $this->source)); } list($actualWidth, $actualHeight, $imageType) = $imageData; // Check image type support $supportedTypes = array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG); - if ($this->sourceType != self::SOURCE_GD) { + if ($this->sourceType != self::SOURCE_GD && $this->sourceType != self::SOURCE_STRING) { $supportedTypes = array_merge($supportedTypes, array(IMAGETYPE_BMP, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM)); } if (!in_array($imageType, $supportedTypes)) { @@ -407,22 +436,25 @@ class Image extends AbstractElement /** * Set source type. - * - * @param string $source - * @return void */ - private function setSourceType($source) + private function setSourceType() { - if (stripos(strrev($source), strrev('.php')) === 0) { + if (stripos(strrev($this->source), strrev('.php')) === 0) { $this->memoryImage = true; $this->sourceType = self::SOURCE_GD; - } elseif (strpos($source, 'zip://') !== false) { + } elseif (strpos($this->source, 'zip://') !== false) { $this->memoryImage = false; $this->sourceType = self::SOURCE_ARCHIVE; - } elseif (filter_var($source, FILTER_VALIDATE_URL) !== false) { + } elseif (filter_var($this->source, FILTER_VALIDATE_URL) !== false) { $this->memoryImage = true; - $this->sourceType = self::SOURCE_GD; - } elseif (@file_exists($source)) { + if (strpos($this->source, 'https') === 0) { + $fileContent = file_get_contents($this->source); + $this->source = $fileContent; + $this->sourceType = self::SOURCE_STRING; + } else { + $this->sourceType = self::SOURCE_GD; + } + } elseif (@file_exists($this->source)) { $this->memoryImage = false; $this->sourceType = self::SOURCE_LOCAL; } else { @@ -438,9 +470,9 @@ class Image extends AbstractElement * * @param string $source * - * @return array|null - * * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException + * + * @return array|null */ private function getArchiveImageSize($source) { @@ -450,12 +482,12 @@ class Image extends AbstractElement $tempFilename = tempnam(Settings::getTempDir(), 'PHPWordImage'); if (false === $tempFilename) { - throw new CreateTemporaryFileException(); + throw new CreateTemporaryFileException(); // @codeCoverageIgnore } $zip = new ZipArchive(); if ($zip->open($zipFilename) !== false) { - if ($zip->locateName($imageFilename)) { + if ($zip->locateName($imageFilename) !== false) { $imageContent = $zip->getFromName($imageFilename); if ($imageContent !== false) { file_put_contents($tempFilename, $imageContent); @@ -471,43 +503,43 @@ class Image extends AbstractElement /** * get image size from string - * + * * @param string $source - * + * * @codeCoverageIgnore this method is just a replacement for getimagesizefromstring which exists only as of PHP 5.4 */ private function getStringImageSize($source) { + $result = false; if (!function_exists('getimagesizefromstring')) { - $uri = 'data://application/octet-stream;base64,' . base64_encode($source); - return @getimagesize($uri); + $uri = 'data://application/octet-stream;base64,' . base64_encode($source); + $result = @getimagesize($uri); } else { - return @getimagesizefromstring($source); + $result = @getimagesizefromstring($source); } - return false; + + return $result; } /** * Set image functions and extensions. - * - * @return void */ private function setFunctions() { switch ($this->imageType) { case 'image/png': - $this->imageCreateFunc = 'imagecreatefrompng'; + $this->imageCreateFunc = $this->sourceType == self::SOURCE_STRING ? 'imagecreatefromstring' : 'imagecreatefrompng'; $this->imageFunc = 'imagepng'; $this->imageExtension = 'png'; break; case 'image/gif': - $this->imageCreateFunc = 'imagecreatefromgif'; + $this->imageCreateFunc = $this->sourceType == self::SOURCE_STRING ? 'imagecreatefromstring' : 'imagecreatefromgif'; $this->imageFunc = 'imagegif'; $this->imageExtension = 'gif'; break; case 'image/jpeg': case 'image/jpg': - $this->imageCreateFunc = 'imagecreatefromjpeg'; + $this->imageCreateFunc = $this->sourceType == self::SOURCE_STRING ? 'imagecreatefromstring' : 'imagecreatefromjpeg'; $this->imageFunc = 'imagejpeg'; $this->imageExtension = 'jpg'; break; @@ -525,9 +557,8 @@ class Image extends AbstractElement /** * Set proportional width/height if one dimension not available. * - * @param integer $actualWidth - * @param integer $actualHeight - * @return void + * @param int $actualWidth + * @param int $actualHeight */ private function setProportionalSize($actualWidth, $actualHeight) { diff --git a/src/PhpWord/Element/Line.php b/src/PhpWord/Element/Line.php index 3e94a3a6..7e40b940 100644 --- a/src/PhpWord/Element/Line.php +++ b/src/PhpWord/Element/Line.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/Link.php b/src/PhpWord/Element/Link.php index 4a72e167..2bec32dd 100644 --- a/src/PhpWord/Element/Link.php +++ b/src/PhpWord/Element/Link.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -75,6 +75,7 @@ class Link extends AbstractElement * @param string $text * @param mixed $fontStyle * @param mixed $paragraphStyle + * @param bool $internal */ public function __construct($source, $text = null, $fontStyle = null, $paragraphStyle = null, $internal = false) { @@ -83,7 +84,6 @@ class Link extends AbstractElement $this->fontStyle = $this->setNewStyle(new Font('text'), $fontStyle); $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); $this->internal = $internal; - return $this; } /** diff --git a/src/PhpWord/Element/ListItem.php b/src/PhpWord/Element/ListItem.php index 25ace090..8b064c47 100644 --- a/src/PhpWord/Element/ListItem.php +++ b/src/PhpWord/Element/ListItem.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -62,7 +62,7 @@ class ListItem extends AbstractElement // Version >= 0.10.0 will pass numbering style name. Older version will use old method if (!is_null($listStyle) && is_string($listStyle)) { - $this->style = new ListItemStyle($listStyle); + $this->style = new ListItemStyle($listStyle); // @codeCoverageIgnore } else { $this->style = $this->setNewStyle(new ListItemStyle(), $listStyle, true); } diff --git a/src/PhpWord/Element/ListItemRun.php b/src/PhpWord/Element/ListItemRun.php index 53440db6..6e48a690 100644 --- a/src/PhpWord/Element/ListItemRun.php +++ b/src/PhpWord/Element/ListItemRun.php @@ -10,15 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * -* @link https://github.com/PHPOffice/PHPWord -* @copyright 2010-2016 PHPWord contributors +* @see https://github.com/PHPOffice/PHPWord +* @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Element; use PhpOffice\PhpWord\Style\ListItem as ListItemStyle; -use PhpOffice\PhpWord\Style\Paragraph; /** * List item element @@ -61,7 +60,7 @@ class ListItemRun extends TextRun } else { $this->style = $this->setNewStyle(new ListItemStyle(), $listStyle, true); } - $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); + parent::__construct($paragraphStyle); } /** @@ -74,10 +73,10 @@ class ListItemRun extends TextRun return $this->style; } - /** + /** * Get ListItem depth. - * - * @return int + * + * @return int */ public function getDepth() { diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/OLEObject.php similarity index 92% rename from src/PhpWord/Element/Object.php rename to src/PhpWord/Element/OLEObject.php index 7285030c..1a17b747 100644 --- a/src/PhpWord/Element/Object.php +++ b/src/PhpWord/Element/OLEObject.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,9 +21,9 @@ use PhpOffice\PhpWord\Exception\InvalidObjectException; use PhpOffice\PhpWord\Style\Image as ImageStyle; /** - * Object element + * OLEObject element */ -class Object extends AbstractElement +class OLEObject extends AbstractElement { /** * Ole-Object Src @@ -83,10 +83,10 @@ class Object extends AbstractElement $this->style = $this->setNewStyle(new ImageStyle(), $style, true); $this->icon = realpath(__DIR__ . "/../resources/{$ext}.png"); - return $this; - } else { - throw new InvalidObjectException(); + return; } + + throw new InvalidObjectException(); } /** @@ -133,7 +133,6 @@ class Object extends AbstractElement * Set Image Relation ID. * * @param int $rId - * @return void */ public function setImageRelationId($rId) { diff --git a/src/PhpWord/Element/PageBreak.php b/src/PhpWord/Element/PageBreak.php index d9d4bc64..1e2ada80 100644 --- a/src/PhpWord/Element/PageBreak.php +++ b/src/PhpWord/Element/PageBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/PreserveText.php b/src/PhpWord/Element/PreserveText.php index 65e17e35..374f1a99 100644 --- a/src/PhpWord/Element/PreserveText.php +++ b/src/PhpWord/Element/PreserveText.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,7 +29,7 @@ class PreserveText extends AbstractElement /** * Text content * - * @var string + * @var string|array */ private $text; @@ -47,14 +47,12 @@ class PreserveText extends AbstractElement */ private $paragraphStyle; - /** * Create a new Preserve Text Element * * @param string $text * @param mixed $fontStyle * @param mixed $paragraphStyle - * @return self */ public function __construct($text = null, $fontStyle = null, $paragraphStyle = null) { @@ -66,8 +64,6 @@ class PreserveText extends AbstractElement if (isset($matches[0])) { $this->text = $matches; } - - return $this; } /** @@ -93,7 +89,7 @@ class PreserveText extends AbstractElement /** * Get Text content * - * @return string + * @return string|array */ public function getText() { diff --git a/src/PhpWord/Element/Row.php b/src/PhpWord/Element/Row.php index 05fde7e4..da4dfe5d 100644 --- a/src/PhpWord/Element/Row.php +++ b/src/PhpWord/Element/Row.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/SDT.php b/src/PhpWord/Element/SDT.php index 58a477d9..a866d1bd 100644 --- a/src/PhpWord/Element/SDT.php +++ b/src/PhpWord/Element/SDT.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -45,16 +45,30 @@ class SDT extends Text */ private $listItems = array(); + /** + * Alias + * + * @var string + */ + private $alias; + + /** + * Tag + * + * @var string + */ + private $tag; + /** * Create new instance * * @param string $type * @param mixed $fontStyle * @param mixed $paragraphStyle - * @return self */ public function __construct($type, $fontStyle = null, $paragraphStyle = null) { + parent::__construct(null, $fontStyle, $paragraphStyle); $this->setType($type); } @@ -127,4 +141,50 @@ class SDT extends Text return $this; } + + /** + * Get tag + * + * @return string + */ + public function getTag() + { + return $this->tag; + } + + /** + * Set tag + * + * @param string $tag + * @return self + */ + public function setTag($tag) + { + $this->tag = $tag; + + return $this; + } + + /** + * Get alias + * + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * Set alias + * + * @param string $alias + * @return self + */ + public function setAlias($alias) + { + $this->alias = $alias; + + return $this; + } } diff --git a/src/PhpWord/Element/Section.php b/src/PhpWord/Element/Section.php index 1e926d2f..d612fc01 100644 --- a/src/PhpWord/Element/Section.php +++ b/src/PhpWord/Element/Section.php @@ -10,13 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Element; +use PhpOffice\PhpWord\ComplexType\FootnoteProperties; use PhpOffice\PhpWord\Style\Section as SectionStyle; class Section extends AbstractContainer @@ -47,6 +48,13 @@ class Section extends AbstractContainer */ private $footers = array(); + /** + * The properties for the footnote of this section + * + * @var FootnoteProperties + */ + private $footnoteProperties; + /** * Create new instance * @@ -65,7 +73,6 @@ class Section extends AbstractContainer * Set section style. * * @param array $style - * @return void */ public function setStyle($style = null) { @@ -78,8 +85,6 @@ class Section extends AbstractContainer * Get section style * * @return \PhpOffice\PhpWord\Style\Section - * - * @codeCoverageIgnore */ public function getStyle() { @@ -118,8 +123,6 @@ class Section extends AbstractContainer * Get header elements * * @return Header[] - * - * @codeCoverageIgnore */ public function getHeaders() { @@ -130,21 +133,39 @@ class Section extends AbstractContainer * Get footer elements * * @return Footer[] - * - * @codeCoverageIgnore */ public function getFooters() { return $this->footers; } + /** + * Get the footnote properties + * + * @return FootnoteProperties + */ + public function getFootnotePropoperties() + { + return $this->footnoteProperties; + } + + /** + * Set the footnote properties + * + * @param FootnoteProperties $footnoteProperties + */ + public function setFootnoteProperties(FootnoteProperties $footnoteProperties = null) + { + $this->footnoteProperties = $footnoteProperties; + } + /** * Is there a header for this section that is for the first page only? * * If any of the Header instances have a type of Header::FIRST then this method returns true. * False otherwise. * - * @return boolean + * @return bool */ public function hasDifferentFirstPage() { @@ -153,6 +174,12 @@ class Section extends AbstractContainer return true; } } + foreach ($this->footers as $footer) { + if ($footer->getType() == Header::FIRST) { + return true; + } + } + return false; } @@ -162,11 +189,11 @@ class Section extends AbstractContainer * @since 0.10.0 * * @param string $type - * @param boolean $header - * - * @return Header|Footer + * @param bool $header * * @throws \Exception + * + * @return Header|Footer */ private function addHeaderFooter($type = Header::AUTO, $header = true) { @@ -182,11 +209,10 @@ class Section extends AbstractContainer $container->setPhpWord($this->phpWord); $collection[$index] = $container; - return $container; - } else { - throw new \Exception('Invalid header/footer type.'); - } + return $container; + } + throw new \Exception('Invalid header/footer type.'); } /** @@ -258,8 +284,8 @@ class Section extends AbstractContainer { if (empty($this->footers)) { return null; - } else { - return $this->footers[1]; } + + return $this->footers[1]; } } diff --git a/src/PhpWord/Element/Shape.php b/src/PhpWord/Element/Shape.php index 4717afb8..d143c9b6 100644 --- a/src/PhpWord/Element/Shape.php +++ b/src/PhpWord/Element/Shape.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/TOC.php b/src/PhpWord/Element/TOC.php index 54ae3844..c51d0e6b 100644 --- a/src/PhpWord/Element/TOC.php +++ b/src/PhpWord/Element/TOC.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -36,7 +36,7 @@ class TOC extends AbstractElement /** * Font style * - * @var \PhpOffice\PhpWord\Style\Font|array|string + * @var \PhpOffice\PhpWord\Style\Font|string */ private $fontStyle; @@ -54,14 +54,13 @@ class TOC extends AbstractElement */ private $maxDepth = 9; - /** * Create a new Table-of-Contents Element * * @param mixed $fontStyle * @param array $tocStyle - * @param integer $minDepth - * @param integer $maxDepth + * @param int $minDepth + * @param int $maxDepth */ public function __construct($fontStyle = null, $tocStyle = null, $minDepth = 1, $maxDepth = 9) { @@ -121,7 +120,7 @@ class TOC extends AbstractElement /** * Get Font Style * - * @return \PhpOffice\PhpWord\Style\Font + * @return \PhpOffice\PhpWord\Style\Font|string */ public function getStyleFont() { @@ -132,7 +131,6 @@ class TOC extends AbstractElement * Set max depth. * * @param int $value - * @return void */ public function setMaxDepth($value) { @@ -153,7 +151,6 @@ class TOC extends AbstractElement * Set min depth. * * @param int $value - * @return void */ public function setMinDepth($value) { diff --git a/src/PhpWord/Element/Table.php b/src/PhpWord/Element/Table.php index 357af37a..44fe3744 100644 --- a/src/PhpWord/Element/Table.php +++ b/src/PhpWord/Element/Table.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -121,7 +121,6 @@ class Table extends AbstractElement * Set table width. * * @param int $width - * @return void */ public function setWidth($width) { @@ -136,18 +135,40 @@ class Table extends AbstractElement public function countColumns() { $columnCount = 0; - if (is_array($this->rows)) { - $rowCount = count($this->rows); - for ($i = 0; $i < $rowCount; $i++) { - /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */ - $row = $this->rows[$i]; - $cellCount = count($row->getCells()); - if ($columnCount < $cellCount) { - $columnCount = $cellCount; - } + + $rowCount = count($this->rows); + for ($i = 0; $i < $rowCount; $i++) { + /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */ + $row = $this->rows[$i]; + $cellCount = count($row->getCells()); + if ($columnCount < $cellCount) { + $columnCount = $cellCount; } } return $columnCount; } + + /** + * The first declared cell width for each column + * + * @return int[] + */ + public function findFirstDefinedCellWidths() + { + $cellWidths = array(); + + foreach ($this->rows as $row) { + $cells = $row->getCells(); + if (count($cells) <= count($cellWidths)) { + continue; + } + $cellWidths = array(); + foreach ($cells as $cell) { + $cellWidths[] = $cell->getWidth(); + } + } + + return $cellWidths; + } } diff --git a/src/PhpWord/Element/Text.php b/src/PhpWord/Element/Text.php index 0de9cdea..f4d7f081 100644 --- a/src/PhpWord/Element/Text.php +++ b/src/PhpWord/Element/Text.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -105,12 +105,12 @@ class Text extends AbstractElement public function setParagraphStyle($style = null) { if (is_array($style)) { - $this->paragraphStyle = new Paragraph; + $this->paragraphStyle = new Paragraph(); $this->paragraphStyle->setStyleByArray($style); } elseif ($style instanceof Paragraph) { $this->paragraphStyle = $style; } elseif (null === $style) { - $this->paragraphStyle = new Paragraph; + $this->paragraphStyle = new Paragraph(); } else { $this->paragraphStyle = $style; } diff --git a/src/PhpWord/Element/TextBox.php b/src/PhpWord/Element/TextBox.php index 4a1e5131..b9f274d6 100644 --- a/src/PhpWord/Element/TextBox.php +++ b/src/PhpWord/Element/TextBox.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Element/TextBreak.php b/src/PhpWord/Element/TextBreak.php index 893fa875..385fec5a 100644 --- a/src/PhpWord/Element/TextBreak.php +++ b/src/PhpWord/Element/TextBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -74,6 +74,7 @@ class TextBreak extends AbstractElement $this->fontStyle = $style; $this->setParagraphStyle($paragraphStyle); } + return $this->fontStyle; } @@ -96,13 +97,14 @@ class TextBreak extends AbstractElement public function setParagraphStyle($style = null) { if (is_array($style)) { - $this->paragraphStyle = new Paragraph; + $this->paragraphStyle = new Paragraph(); $this->paragraphStyle->setStyleByArray($style); } elseif ($style instanceof Paragraph) { $this->paragraphStyle = $style; } else { $this->paragraphStyle = $style; } + return $this->paragraphStyle; } diff --git a/src/PhpWord/Element/TextRun.php b/src/PhpWord/Element/TextRun.php index c2ce4f99..9af55d46 100644 --- a/src/PhpWord/Element/TextRun.php +++ b/src/PhpWord/Element/TextRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -43,7 +43,7 @@ class TextRun extends AbstractContainer */ public function __construct($paragraphStyle = null) { - $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); + $this->paragraphStyle = $this->setParagraphStyle($paragraphStyle); } /** @@ -55,4 +55,26 @@ class TextRun extends AbstractContainer { return $this->paragraphStyle; } + + /** + * Set Paragraph style + * + * @param string|array|\PhpOffice\PhpWord\Style\Paragraph $style + * @return string|\PhpOffice\PhpWord\Style\Paragraph + */ + public function setParagraphStyle($style = null) + { + if (is_array($style)) { + $this->paragraphStyle = new Paragraph(); + $this->paragraphStyle->setStyleByArray($style); + } elseif ($style instanceof Paragraph) { + $this->paragraphStyle = $style; + } elseif (null === $style) { + $this->paragraphStyle = new Paragraph(); + } else { + $this->paragraphStyle = $style; + } + + return $this->paragraphStyle; + } } diff --git a/src/PhpWord/Element/Title.php b/src/PhpWord/Element/Title.php index eabb1feb..d01f7f33 100644 --- a/src/PhpWord/Element/Title.php +++ b/src/PhpWord/Element/Title.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,7 +28,7 @@ class Title extends AbstractElement /** * Title Text content * - * @var string + * @var string|TextRun */ private $text; @@ -56,18 +56,24 @@ class Title extends AbstractElement /** * Create a new Title Element * - * @param string $text + * @param string|TextRun $text * @param int $depth */ public function __construct($text, $depth = 1) { - $this->text = CommonText::toUTF8($text); - $this->depth = $depth; - if (array_key_exists("Heading_{$this->depth}", Style::getStyles())) { - $this->style = "Heading{$this->depth}"; + if (is_string($text)) { + $this->text = CommonText::toUTF8($text); + } elseif ($text instanceof TextRun) { + $this->text = $text; + } else { + throw new \InvalidArgumentException('Invalid text, should be a string or a TextRun'); } - return $this; + $this->depth = $depth; + $styleName = $depth === 0 ? 'Title' : "Heading_{$this->depth}"; + if (array_key_exists($styleName, Style::getStyles())) { + $this->style = str_replace('_', '', $styleName); + } } /** @@ -83,7 +89,7 @@ class Title extends AbstractElement /** * Get depth * - * @return integer + * @return int */ public function getDepth() { diff --git a/src/PhpWord/Element/TrackChange.php b/src/PhpWord/Element/TrackChange.php new file mode 100644 index 00000000..410ffb7c --- /dev/null +++ b/src/PhpWord/Element/TrackChange.php @@ -0,0 +1,101 @@ +changeType = $changeType; + $this->author = $author; + if ($date !== null) { + $this->date = ($date instanceof \DateTime) ? $date : new \DateTime('@' . $date); + } + } + + /** + * Get TrackChange Author + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Get TrackChange Date + * + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } + + /** + * Get the Change type + * + * @return string + */ + public function getChangeType() + { + return $this->changeType; + } +} diff --git a/src/PhpWord/Escaper/AbstractEscaper.php b/src/PhpWord/Escaper/AbstractEscaper.php index 6ddcbb51..1575c069 100644 --- a/src/PhpWord/Escaper/AbstractEscaper.php +++ b/src/PhpWord/Escaper/AbstractEscaper.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * @since 0.13.0 - * + * * @codeCoverageIgnore */ abstract class AbstractEscaper implements EscaperInterface diff --git a/src/PhpWord/Escaper/EscaperInterface.php b/src/PhpWord/Escaper/EscaperInterface.php index c34cf370..deb2cfbc 100644 --- a/src/PhpWord/Escaper/EscaperInterface.php +++ b/src/PhpWord/Escaper/EscaperInterface.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * @since 0.13.0 - * + * * @codeCoverageIgnore */ interface EscaperInterface diff --git a/src/PhpWord/Escaper/RegExp.php b/src/PhpWord/Escaper/RegExp.php index de510bcf..f69aad82 100644 --- a/src/PhpWord/Escaper/RegExp.php +++ b/src/PhpWord/Escaper/RegExp.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * @since 0.13.0 - * + * * @codeCoverageIgnore */ class RegExp extends AbstractEscaper diff --git a/src/PhpWord/Escaper/Rtf.php b/src/PhpWord/Escaper/Rtf.php index 6f83604d..b8e0b216 100644 --- a/src/PhpWord/Escaper/Rtf.php +++ b/src/PhpWord/Escaper/Rtf.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * @since 0.13.0 - * + * * @codeCoverageIgnore */ class Rtf extends AbstractEscaper @@ -28,9 +28,9 @@ class Rtf extends AbstractEscaper { if (20 > $code || $code >= 80) { return '{\u' . $code . '}'; - } else { - return chr($code); } + + return chr($code); } protected function escapeMultibyteCharacter($code) @@ -40,6 +40,7 @@ class Rtf extends AbstractEscaper /** * @see http://www.randomchaos.com/documents/?source=php_and_unicode + * @param string $input */ protected function escapeSingleValue($input) { @@ -57,9 +58,9 @@ class Rtf extends AbstractEscaper if (0 == count($bytes)) { if ($asciiCode < 224) { $numberOfBytes = 2; - } else if ($asciiCode < 240) { + } elseif ($asciiCode < 240) { $numberOfBytes = 3; - } else if ($asciiCode < 248) { + } elseif ($asciiCode < 248) { $numberOfBytes = 4; } } diff --git a/src/PhpWord/Escaper/Xml.php b/src/PhpWord/Escaper/Xml.php index 274cade5..a769c5e1 100644 --- a/src/PhpWord/Escaper/Xml.php +++ b/src/PhpWord/Escaper/Xml.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * @since 0.13.0 - * + * * @codeCoverageIgnore */ class Xml extends AbstractEscaper diff --git a/src/PhpWord/Exception/CopyFileException.php b/src/PhpWord/Exception/CopyFileException.php index c172657f..d1c3bd01 100644 --- a/src/PhpWord/Exception/CopyFileException.php +++ b/src/PhpWord/Exception/CopyFileException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,10 +23,10 @@ namespace PhpOffice\PhpWord\Exception; final class CopyFileException extends Exception { /** - * @param string $source The fully qualified source file name. - * @param string $destination The fully qualified destination file name. - * @param integer $code The user defined exception code. - * @param \Exception $previous The previous exception used for the exception chaining. + * @param string $source The fully qualified source file name + * @param string $destination The fully qualified destination file name + * @param int $code The user defined exception code + * @param \Exception $previous The previous exception used for the exception chaining */ final public function __construct($source, $destination, $code = 0, \Exception $previous = null) { diff --git a/src/PhpWord/Exception/CreateTemporaryFileException.php b/src/PhpWord/Exception/CreateTemporaryFileException.php index 67d969ba..c8a06429 100644 --- a/src/PhpWord/Exception/CreateTemporaryFileException.php +++ b/src/PhpWord/Exception/CreateTemporaryFileException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,8 +23,8 @@ namespace PhpOffice\PhpWord\Exception; final class CreateTemporaryFileException extends Exception { /** - * @param integer $code The user defined exception code. - * @param \Exception $previous The previous exception used for the exception chaining. + * @param int $code The user defined exception code + * @param \Exception $previous The previous exception used for the exception chaining */ final public function __construct($code = 0, \Exception $previous = null) { diff --git a/src/PhpWord/Exception/Exception.php b/src/PhpWord/Exception/Exception.php index a9c49f7f..d874625c 100644 --- a/src/PhpWord/Exception/Exception.php +++ b/src/PhpWord/Exception/Exception.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Exception/InvalidImageException.php b/src/PhpWord/Exception/InvalidImageException.php index 21c885ee..07c96681 100644 --- a/src/PhpWord/Exception/InvalidImageException.php +++ b/src/PhpWord/Exception/InvalidImageException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Exception/InvalidObjectException.php b/src/PhpWord/Exception/InvalidObjectException.php index ad564d47..d8fef961 100644 --- a/src/PhpWord/Exception/InvalidObjectException.php +++ b/src/PhpWord/Exception/InvalidObjectException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Exception/InvalidStyleException.php b/src/PhpWord/Exception/InvalidStyleException.php index 44980842..58c1961d 100644 --- a/src/PhpWord/Exception/InvalidStyleException.php +++ b/src/PhpWord/Exception/InvalidStyleException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Exception/UnsupportedImageTypeException.php b/src/PhpWord/Exception/UnsupportedImageTypeException.php index 1b09bc8f..ee270653 100644 --- a/src/PhpWord/Exception/UnsupportedImageTypeException.php +++ b/src/PhpWord/Exception/UnsupportedImageTypeException.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/IOFactory.php b/src/PhpWord/IOFactory.php index c868841a..3929f485 100644 --- a/src/PhpWord/IOFactory.php +++ b/src/PhpWord/IOFactory.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,9 +29,9 @@ abstract class IOFactory * @param PhpWord $phpWord * @param string $name * - * @return WriterInterface - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return WriterInterface */ public static function createWriter(PhpWord $phpWord, $name = 'Word2007') { @@ -49,9 +49,9 @@ abstract class IOFactory * * @param string $name * - * @return ReaderInterface - * * @throws Exception + * + * @return ReaderInterface */ public static function createReader($name = 'Word2007') { @@ -65,19 +65,19 @@ abstract class IOFactory * @param string $name * @param \PhpOffice\PhpWord\PhpWord $phpWord * - * @return \PhpOffice\PhpWord\Writer\WriterInterface|\PhpOffice\PhpWord\Reader\ReaderInterface - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return \PhpOffice\PhpWord\Writer\WriterInterface|\PhpOffice\PhpWord\Reader\ReaderInterface */ private static function createObject($type, $name, $phpWord = null) { $class = "PhpOffice\\PhpWord\\{$type}\\{$name}"; if (class_exists($class) && self::isConcreteClass($class)) { return new $class($phpWord); - } else { - throw new Exception("\"{$name}\" is not a valid {$type}."); } + throw new Exception("\"{$name}\" is not a valid {$type}."); } + /** * Loads PhpWord from file * @@ -89,8 +89,10 @@ abstract class IOFactory { /** @var \PhpOffice\PhpWord\Reader\ReaderInterface $reader */ $reader = self::createReader($readerName); + return $reader->load($filename); } + /** * Check if it's a concrete class (not abstract nor interface) * @@ -100,6 +102,7 @@ abstract class IOFactory private static function isConcreteClass($class) { $reflection = new \ReflectionClass($class); + return !$reflection->isAbstract() && !$reflection->isInterface(); } } diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index df337854..cc1b2903 100644 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -43,9 +43,9 @@ class Media * @param string $source * @param \PhpOffice\PhpWord\Element\Image $image * - * @return integer - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return int */ public static function addElement($container, $mediaType, $source, Image $image = null) { @@ -83,12 +83,10 @@ class Media $image->setTarget($target); $image->setMediaIndex($mediaTypeCount); break; - // Objects case 'object': $target = "{$container}_oleObject{$mediaTypeCount}.bin"; break; - // Links case 'link': $target = $source; @@ -100,15 +98,17 @@ class Media $mediaData['type'] = $mediaType; $mediaData['rID'] = $rId; self::$elements[$container][$mediaId] = $mediaData; + return $rId; - } else { - $mediaData = self::$elements[$container][$mediaId]; - if (!is_null($image)) { - $image->setTarget($mediaData['target']); - $image->setMediaIndex($mediaData['mediaIndex']); - } - return $mediaData['rID']; } + + $mediaData = self::$elements[$container][$mediaId]; + if (!is_null($image)) { + $image->setTarget($mediaData['target']); + $image->setMediaIndex($mediaData['mediaIndex']); + } + + return $mediaData['rID']; } /** @@ -116,7 +116,7 @@ class Media * * @param string $container section|headerx|footerx|footnote|endnote * @param string $mediaType image|object|link - * @return integer + * @return int * @since 0.10.0 */ public static function countElements($container, $mediaType = null) @@ -157,13 +157,15 @@ class Media $elements[$key] = $val; } } + return $elements; - } else { - if (!isset(self::$elements[$container])) { - return $elements; - } - return self::getElementsByType($container, $type); } + + if (!isset(self::$elements[$container])) { + return $elements; + } + + return self::getElementsByType($container, $type); } /** @@ -208,7 +210,7 @@ class Media * @param string $type * @param \PhpOffice\PhpWord\Element\Image $image * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -224,7 +226,7 @@ class Media * * @param string $linkSrc * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -256,7 +258,7 @@ class Media * * @param string $key * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -270,11 +272,11 @@ class Media * * @deprecated 0.10.0 * - * @param integer $headerCount + * @param int $headerCount * @param string $src * @param \PhpOffice\PhpWord\Element\Image $image * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -290,7 +292,7 @@ class Media * * @param string $key * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -318,11 +320,11 @@ class Media * * @deprecated 0.10.0 * - * @param integer $footerCount + * @param int $footerCount * @param string $src * @param \PhpOffice\PhpWord\Element\Image $image * - * @return integer + * @return int * * @codeCoverageIgnore */ @@ -338,7 +340,7 @@ class Media * * @param string $key * - * @return integer + * @return int * * @codeCoverageIgnore */ diff --git a/src/PhpWord/Metadata/Compatibility.php b/src/PhpWord/Metadata/Compatibility.php index eb93274d..bf0363aa 100644 --- a/src/PhpWord/Metadata/Compatibility.php +++ b/src/PhpWord/Metadata/Compatibility.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,7 +21,7 @@ namespace PhpOffice\PhpWord\Metadata; * Compatibility setting class * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/t-w_CT_Compat.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_Compat.html */ class Compatibility { @@ -33,7 +33,7 @@ class Compatibility * 15 = 2013 * * @var int - * @link http://msdn.microsoft.com/en-us/library/dd909048%28v=office.12%29.aspx + * @see http://msdn.microsoft.com/en-us/library/dd909048%28v=office.12%29.aspx */ private $ooxmlVersion = 12; diff --git a/src/PhpWord/Metadata/DocInfo.php b/src/PhpWord/Metadata/DocInfo.php index 63a7d515..27ef89ae 100644 --- a/src/PhpWord/Metadata/DocInfo.php +++ b/src/PhpWord/Metadata/DocInfo.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -119,17 +119,17 @@ class DocInfo */ public function __construct() { - $this->creator = ''; + $this->creator = ''; $this->lastModifiedBy = $this->creator; - $this->created = time(); - $this->modified = time(); - $this->title = ''; - $this->subject = ''; - $this->description = ''; - $this->keywords = ''; - $this->category = ''; - $this->company = ''; - $this->manager = ''; + $this->created = time(); + $this->modified = time(); + $this->title = ''; + $this->subject = ''; + $this->description = ''; + $this->keywords = ''; + $this->category = ''; + $this->company = ''; + $this->manager = ''; } /** @@ -399,7 +399,7 @@ class DocInfo * Check if a Custom Property is defined * * @param string $propertyName - * @return boolean + * @return bool */ public function isCustomPropertySet($propertyName) { @@ -410,15 +410,15 @@ class DocInfo * Get a Custom Property Value * * @param string $propertyName - * @return string + * @return mixed */ public function getCustomPropertyValue($propertyName) { if ($this->isCustomPropertySet($propertyName)) { return $this->customProperties[$propertyName]['value']; - } else { - return null; } + + return null; } /** @@ -431,9 +431,9 @@ class DocInfo { if ($this->isCustomPropertySet($propertyName)) { return $this->customProperties[$propertyName]['type']; - } else { - return null; } + + return null; } /** @@ -456,7 +456,7 @@ class DocInfo self::PROPERTY_TYPE_FLOAT, self::PROPERTY_TYPE_STRING, self::PROPERTY_TYPE_DATE, - self::PROPERTY_TYPE_BOOLEAN + self::PROPERTY_TYPE_BOOLEAN, ); if (($propertyType === null) || (!in_array($propertyType, $propertyTypes))) { if ($propertyValue === null) { @@ -467,6 +467,8 @@ class DocInfo $propertyType = self::PROPERTY_TYPE_INTEGER; } elseif (is_bool($propertyValue)) { $propertyType = self::PROPERTY_TYPE_BOOLEAN; + } elseif ($propertyValue instanceof \DateTime) { + $propertyType = self::PROPERTY_TYPE_DATE; } else { $propertyType = self::PROPERTY_TYPE_STRING; } @@ -474,8 +476,9 @@ class DocInfo $this->customProperties[$propertyName] = array( 'value' => $propertyValue, - 'type' => $propertyType + 'type' => $propertyType, ); + return $this; } diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php index 0e2ee7c1..15aa3ff1 100644 --- a/src/PhpWord/Metadata/Protection.php +++ b/src/PhpWord/Metadata/Protection.php @@ -10,30 +10,60 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Metadata; +use PhpOffice\Common\Microsoft\PasswordEncoder; +use PhpOffice\PhpWord\SimpleType\DocProtect; + /** * Document protection class * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/t-w_CT_DocProtect.html - * @todo Password! + * @see http://www.datypic.com/sc/ooxml/t-w_CT_DocProtect.html */ class Protection { /** - * Editing restriction readOnly|comments|trackedChanges|forms + * Editing restriction none|readOnly|comments|trackedChanges|forms * * @var string - * @link http://www.datypic.com/sc/ooxml/a-w_edit-1.html + * @see http://www.datypic.com/sc/ooxml/a-w_edit-1.html */ private $editing; + /** + * password + * + * @var string + */ + private $password; + + /** + * Iterations to Run Hashing Algorithm + * + * @var int + */ + private $spinCount = 100000; + + /** + * Cryptographic Hashing Algorithm (see constants defined in \PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder) + * + * @var string + */ + private $algorithm = PasswordEncoder::ALGORITHM_SHA_1; + + /** + * Salt for Password Verifier + * + * @var string + */ + private $salt; + /** * Create a new instance * @@ -41,7 +71,9 @@ class Protection */ public function __construct($editing = null) { - $this->setEditing($editing); + if ($editing != null) { + $this->setEditing($editing); + } } /** @@ -57,13 +89,111 @@ class Protection /** * Set editing protection * - * @param string $editing + * @param string $editing Any value of \PhpOffice\PhpWord\SimpleType\DocProtect * @return self */ public function setEditing($editing = null) { + DocProtect::validate($editing); $this->editing = $editing; return $this; } + + /** + * Get password + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set password + * + * @param string $password + * @return self + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get count for hash iterations + * + * @return int + */ + public function getSpinCount() + { + return $this->spinCount; + } + + /** + * Set count for hash iterations + * + * @param int $spinCount + * @return self + */ + public function setSpinCount($spinCount) + { + $this->spinCount = $spinCount; + + return $this; + } + + /** + * Get algorithm + * + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * Set algorithm + * + * @param string $algorithm + * @return self + */ + public function setAlgorithm($algorithm) + { + $this->algorithm = $algorithm; + + return $this; + } + + /** + * Get salt + * + * @return string + */ + public function getSalt() + { + return $this->salt; + } + + /** + * Set salt. Salt HAS to be 16 characters, or an exception will be thrown. + * + * @param string $salt + * @throws \InvalidArgumentException + * @return self + */ + public function setSalt($salt) + { + if ($salt !== null && strlen($salt) !== 16) { + throw new \InvalidArgumentException('salt has to be of exactly 16 bytes length'); + } + + $this->salt = $salt; + + return $this; + } } diff --git a/src/PhpWord/Metadata/Settings.php b/src/PhpWord/Metadata/Settings.php new file mode 100644 index 00000000..b1552e02 --- /dev/null +++ b/src/PhpWord/Metadata/Settings.php @@ -0,0 +1,480 @@ +documentProtection == null) { + $this->documentProtection = new Protection(); + } + + return $this->documentProtection; + } + + /** + * @param Protection $documentProtection + */ + public function setDocumentProtection($documentProtection) + { + $this->documentProtection = $documentProtection; + } + + /** + * @return ProofState + */ + public function getProofState() + { + if ($this->proofState == null) { + $this->proofState = new ProofState(); + } + + return $this->proofState; + } + + /** + * @param ProofState $proofState + */ + public function setProofState($proofState) + { + $this->proofState = $proofState; + } + + /** + * Are spelling errors hidden + * + * @return bool + */ + public function hasHideSpellingErrors() + { + return $this->hideSpellingErrors; + } + + /** + * Hide spelling errors + * + * @param bool $hideSpellingErrors + */ + public function setHideSpellingErrors($hideSpellingErrors) + { + $this->hideSpellingErrors = $hideSpellingErrors === null ? true : $hideSpellingErrors; + } + + /** + * Are grammatical errors hidden + * + * @return bool + */ + public function hasHideGrammaticalErrors() + { + return $this->hideGrammaticalErrors; + } + + /** + * Hide grammatical errors + * + * @param bool $hideGrammaticalErrors + */ + public function setHideGrammaticalErrors($hideGrammaticalErrors) + { + $this->hideGrammaticalErrors = $hideGrammaticalErrors === null ? true : $hideGrammaticalErrors; + } + + /** + * @return bool + */ + public function hasEvenAndOddHeaders() + { + return $this->evenAndOddHeaders; + } + + /** + * @param bool $evenAndOddHeaders + */ + public function setEvenAndOddHeaders($evenAndOddHeaders) + { + $this->evenAndOddHeaders = $evenAndOddHeaders === null ? true : $evenAndOddHeaders; + } + + /** + * Get the Visibility of Annotation Types + * + * @return \PhpOffice\PhpWord\ComplexType\TrackChangesView + */ + public function getRevisionView() + { + return $this->revisionView; + } + + /** + * Set the Visibility of Annotation Types + * + * @param TrackChangesView $trackChangesView + */ + public function setRevisionView(TrackChangesView $trackChangesView = null) + { + $this->revisionView = $trackChangesView; + } + + /** + * @return bool + */ + public function hasTrackRevisions() + { + return $this->trackRevisions; + } + + /** + * @param bool $trackRevisions + */ + public function setTrackRevisions($trackRevisions) + { + $this->trackRevisions = $trackRevisions === null ? true : $trackRevisions; + } + + /** + * @return bool + */ + public function hasDoNotTrackMoves() + { + return $this->doNotTrackMoves; + } + + /** + * @param bool $doNotTrackMoves + */ + public function setDoNotTrackMoves($doNotTrackMoves) + { + $this->doNotTrackMoves = $doNotTrackMoves === null ? true : $doNotTrackMoves; + } + + /** + * @return bool + */ + public function hasDoNotTrackFormatting() + { + return $this->doNotTrackFormatting; + } + + /** + * @param bool $doNotTrackFormatting + */ + public function setDoNotTrackFormatting($doNotTrackFormatting) + { + $this->doNotTrackFormatting = $doNotTrackFormatting === null ? true : $doNotTrackFormatting; + } + + /** + * @return mixed + */ + public function getZoom() + { + return $this->zoom; + } + + /** + * @param mixed $zoom + */ + public function setZoom($zoom) + { + if (is_numeric($zoom)) { + // zoom is a percentage + $this->zoom = $zoom; + } else { + Zoom::validate($zoom); + $this->zoom = $zoom; + } + } + + /** + * @return bool + */ + public function hasMirrorMargins() + { + return $this->mirrorMargins; + } + + /** + * @param bool $mirrorMargins + */ + public function setMirrorMargins($mirrorMargins) + { + $this->mirrorMargins = $mirrorMargins; + } + + /** + * Returns the Language + * + * @return Language + */ + public function getThemeFontLang() + { + return $this->themeFontLang; + } + + /** + * sets the Language for this document + * + * @param Language $themeFontLang + */ + public function setThemeFontLang($themeFontLang) + { + $this->themeFontLang = $themeFontLang; + } + + /** + * @return bool + */ + public function hasUpdateFields() + { + return $this->updateFields; + } + + /** + * @param bool $updateFields + */ + public function setUpdateFields($updateFields) + { + $this->updateFields = $updateFields === null ? false : $updateFields; + } + + /** + * Returns the Radix Point for Field Code Evaluation + * + * @return string + */ + public function getDecimalSymbol() + { + return $this->decimalSymbol; + } + + /** + * sets the Radix Point for Field Code Evaluation + * + * @param string $decimalSymbol + */ + public function setDecimalSymbol($decimalSymbol) + { + $this->decimalSymbol = $decimalSymbol; + } + + /** + * @return bool|null + */ + public function hasAutoHyphenation() + { + return $this->autoHyphenation; + } + + /** + * @param bool $autoHyphenation + */ + public function setAutoHyphenation($autoHyphenation) + { + $this->autoHyphenation = (bool) $autoHyphenation; + } + + /** + * @return int|null + */ + public function getConsecutiveHyphenLimit() + { + return $this->consecutiveHyphenLimit; + } + + /** + * @param int $consecutiveHyphenLimit + */ + public function setConsecutiveHyphenLimit($consecutiveHyphenLimit) + { + $this->consecutiveHyphenLimit = (int) $consecutiveHyphenLimit; + } + + /** + * @return float|null + */ + public function getHyphenationZone() + { + return $this->hyphenationZone; + } + + /** + * @param float $hyphenationZone Measurement unit is twip + */ + public function setHyphenationZone($hyphenationZone) + { + $this->hyphenationZone = $hyphenationZone; + } + + /** + * @return null|bool + */ + public function hasDoNotHyphenateCaps() + { + return $this->doNotHyphenateCaps; + } + + /** + * @param bool $doNotHyphenateCaps + */ + public function setDoNotHyphenateCaps($doNotHyphenateCaps) + { + $this->doNotHyphenateCaps = (bool) $doNotHyphenateCaps; + } +} diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php index 0fa76b2f..a78df2c4 100644 --- a/src/PhpWord/PhpWord.php +++ b/src/PhpWord/PhpWord.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -27,16 +27,18 @@ use PhpOffice\PhpWord\Exception\Exception; * @method Collection\Footnotes getFootnotes() * @method Collection\Endnotes getEndnotes() * @method Collection\Charts getCharts() + * @method Collection\Comments getComments() * @method int addBookmark(Element\Bookmark $bookmark) * @method int addTitle(Element\Title $title) * @method int addFootnote(Element\Footnote $footnote) * @method int addEndnote(Element\Endnote $endnote) * @method int addChart(Element\Chart $chart) + * @method int addComment(Element\Comment $comment) * - * @method Style\Paragraph addParagraphStyle(string $styleName, array $styles) + * @method Style\Paragraph addParagraphStyle(string $styleName, mixed $styles) * @method Style\Font addFontStyle(string $styleName, mixed $fontStyle, mixed $paragraphStyle = null) * @method Style\Font addLinkStyle(string $styleName, mixed $styles) - * @method Style\Font addTitleStyle(int $depth, mixed $fontStyle, mixed $paragraphStyle = null) + * @method Style\Font addTitleStyle(mixed $depth, mixed $fontStyle, mixed $paragraphStyle = null) * @method Style\Table addTableStyle(string $styleName, mixed $styleTable, mixed $styleFirstRow = null) * @method Style\Numbering addNumberingStyle(string $styleName, mixed $styles) */ @@ -50,8 +52,17 @@ class PhpWord * @const string|int */ const DEFAULT_FONT_NAME = Settings::DEFAULT_FONT_NAME; + /** + * @deprecated 0.11.0 Use Settings constants + */ const DEFAULT_FONT_SIZE = Settings::DEFAULT_FONT_SIZE; + /** + * @deprecated 0.11.0 Use Settings constants + */ const DEFAULT_FONT_COLOR = Settings::DEFAULT_FONT_COLOR; + /** + * @deprecated 0.11.0 Use Settings constants + */ const DEFAULT_FONT_CONTENT_TYPE = Settings::DEFAULT_FONT_CONTENT_TYPE; /** @@ -83,15 +94,19 @@ class PhpWord */ public function __construct() { + // Reset Media and styles + Media::resetElements(); + Style::resetStyles(); + // Collection - $collections = array('Bookmarks', 'Titles', 'Footnotes', 'Endnotes', 'Charts'); + $collections = array('Bookmarks', 'Titles', 'Footnotes', 'Endnotes', 'Charts', 'Comments'); foreach ($collections as $collection) { $class = 'PhpOffice\\PhpWord\\Collection\\' . $collection; $this->collections[$collection] = new $class(); } // Metadata - $metadata = array('DocInfo', 'Protection', 'Compatibility'); + $metadata = array('DocInfo', 'Settings', 'Compatibility'); foreach ($metadata as $meta) { $class = 'PhpOffice\\PhpWord\\Metadata\\' . $meta; $this->metadata[$meta] = new $class(); @@ -106,9 +121,9 @@ class PhpWord * @param mixed $function * @param mixed $args * - * @return mixed - * * @throws \BadMethodCallException + * + * @return mixed */ public function __call($function, $args) { @@ -118,7 +133,7 @@ class PhpWord $addCollection = array(); $addStyle = array(); - $collections = array('Bookmark', 'Title', 'Footnote', 'Endnote', 'Chart'); + $collections = array('Bookmark', 'Title', 'Footnote', 'Endnote', 'Chart', 'Comment'); foreach ($collections as $collection) { $getCollection[] = strtolower("get{$collection}s"); $addCollection[] = strtolower("add{$collection}"); @@ -170,10 +185,12 @@ class PhpWord * * @return \PhpOffice\PhpWord\Metadata\Protection * @since 0.12.0 + * @deprecated Get the Document protection from PhpWord->getSettings()->getDocumentProtection(); + * @codeCoverageIgnore */ public function getProtection() { - return $this->metadata['Protection']; + return $this->getSettings()->getDocumentProtection(); } /** @@ -187,6 +204,17 @@ class PhpWord return $this->metadata['Compatibility']; } + /** + * Get compatibility + * + * @return \PhpOffice\PhpWord\Metadata\Settings + * @since 0.14.0 + */ + public function getSettings() + { + return $this->metadata['Settings']; + } + /** * Get all sections * @@ -197,6 +225,21 @@ class PhpWord return $this->sections; } + /** + * Returns the section at the requested position + * + * @param int $index + * @return \PhpOffice\PhpWord\Element\Section|null + */ + public function getSection($index) + { + if (array_key_exists($index, $this->sections)) { + return $this->sections[$index]; + } + + return null; + } + /** * Create new section * @@ -212,6 +255,17 @@ class PhpWord return $section; } + /** + * Sorts the sections using the callable passed + * + * @see http://php.net/manual/en/function.usort.php for usage + * @param callable $sorter + */ + public function sortSections($sorter) + { + usort($this->sections, $sorter); + } + /** * Get default font name * @@ -226,7 +280,6 @@ class PhpWord * Set default font name. * * @param string $fontName - * @return void */ public function setDefaultFontName($fontName) { @@ -236,7 +289,7 @@ class PhpWord /** * Get default font size * - * @return integer + * @return int */ public function getDefaultFontSize() { @@ -247,7 +300,6 @@ class PhpWord * Set default font size. * * @param int $fontSize - * @return void */ public function setDefaultFontSize($fontSize) { @@ -270,21 +322,20 @@ class PhpWord * * @deprecated 0.12.0 Use `new TemplateProcessor($documentTemplate)` instead. * - * @param string $filename Fully qualified filename. - * - * @return TemplateProcessor + * @param string $filename Fully qualified filename * * @throws \PhpOffice\PhpWord\Exception\Exception * + * @return TemplateProcessor + * * @codeCoverageIgnore */ public function loadTemplate($filename) { if (file_exists($filename)) { return new TemplateProcessor($filename); - } else { - throw new Exception("Template file {$filename} not found."); } + throw new Exception("Template file {$filename} not found."); } /** @@ -310,7 +361,7 @@ class PhpWord $writer = IOFactory::createWriter($this, $format); if ($download === true) { - header("Content-Description: File Transfer"); + header('Content-Description: File Transfer'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Type: ' . $mime[$format]); header('Content-Transfer-Encoding: binary'); diff --git a/src/PhpWord/Reader/AbstractReader.php b/src/PhpWord/Reader/AbstractReader.php index 93288c3b..7db285f7 100644 --- a/src/PhpWord/Reader/AbstractReader.php +++ b/src/PhpWord/Reader/AbstractReader.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -62,6 +62,7 @@ abstract class AbstractReader implements ReaderInterface public function setReadDataOnly($value = true) { $this->readDataOnly = $value; + return $this; } @@ -70,21 +71,21 @@ abstract class AbstractReader implements ReaderInterface * * @param string $filename * - * @return resource - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return resource */ protected function openFile($filename) { // Check if file exists if (!file_exists($filename) || !is_readable($filename)) { - throw new Exception("Could not open " . $filename . " for reading! File does not exist."); + throw new Exception("Could not open $filename for reading! File does not exist."); } // Open file $this->fileHandle = fopen($filename, 'r'); if ($this->fileHandle === false) { - throw new Exception("Could not open file " . $filename . " for reading."); + throw new Exception("Could not open file $filename for reading."); } } diff --git a/src/PhpWord/Reader/HTML.php b/src/PhpWord/Reader/HTML.php index 824573e9..db9f2089 100644 --- a/src/PhpWord/Reader/HTML.php +++ b/src/PhpWord/Reader/HTML.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Reader/MsDoc.php b/src/PhpWord/Reader/MsDoc.php index f21b2bf4..187d5b17 100644 --- a/src/PhpWord/Reader/MsDoc.php +++ b/src/PhpWord/Reader/MsDoc.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,8 +19,8 @@ namespace PhpOffice\PhpWord\Reader; use PhpOffice\Common\Drawing; use PhpOffice\PhpWord\PhpWord; -use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Shared\OLERead; +use PhpOffice\PhpWord\Style; /** * Reader for Word97 @@ -164,13 +164,14 @@ class MsDoc extends AbstractReader implements ReaderInterface $arrayCP[$inc] = self::getInt4d($data, $posMem); $posMem += 4; } + return $arrayCP; } /** - * - * @link http://msdn.microsoft.com/en-us/library/dd949344%28v=office.12%29.aspx - * @link https://igor.io/2012/09/24/binary-parsing.html + * @see http://msdn.microsoft.com/en-us/library/dd949344%28v=office.12%29.aspx + * @see https://igor.io/2012/09/24/binary-parsing.html + * @param string $data */ private function readFib($data) { @@ -1095,6 +1096,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $this->arrayFib['lcbColorSchemeMapping'] = self::getInt4d($data, $pos); $pos += 4; } + return $pos; } @@ -1107,11 +1109,11 @@ class MsDoc extends AbstractReader implements ReaderInterface $this->readRecordPlcfSed(); // reading paragraphs - //@link https://github.com/notmasteryet/CompoundFile/blob/ec118f354efebdee9102e41b5b7084fce81125b0/WordFileReader/WordDocument.cs#L86 + //@see https://github.com/notmasteryet/CompoundFile/blob/ec118f354efebdee9102e41b5b7084fce81125b0/WordFileReader/WordDocument.cs#L86 $this->readRecordPlcfBtePapx(); // reading character formattings - //@link https://github.com/notmasteryet/CompoundFile/blob/ec118f354efebdee9102e41b5b7084fce81125b0/WordFileReader/WordDocument.cs#L94 + //@see https://github.com/notmasteryet/CompoundFile/blob/ec118f354efebdee9102e41b5b7084fce81125b0/WordFileReader/WordDocument.cs#L94 $this->readRecordPlcfBteChpx(); $this->generatePhpWord(); @@ -1119,7 +1121,7 @@ class MsDoc extends AbstractReader implements ReaderInterface /** * Section and information about them - * @link : http://msdn.microsoft.com/en-us/library/dd924458%28v=office.12%29.aspx + * @see : http://msdn.microsoft.com/en-us/library/dd924458%28v=office.12%29.aspx */ private function readRecordPlcfSed() { @@ -1133,7 +1135,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $posMem += 4; // PlcfSed : aSed - //@link : http://msdn.microsoft.com/en-us/library/dd950194%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd950194%28v=office.12%29.aspx $numSed = $this->getNumInLcb($this->arrayFib['lcbPlcfSed'], 12); $aSed = array(); @@ -1164,7 +1166,7 @@ class MsDoc extends AbstractReader implements ReaderInterface /** * Specifies the fonts that are used in the document - * @link : http://msdn.microsoft.com/en-us/library/dd943880%28v=office.12%29.aspx + * @see : http://msdn.microsoft.com/en-us/library/dd943880%28v=office.12%29.aspx */ private function readRecordSttbfFfn() { @@ -1215,7 +1217,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } $this->arrayFonts[] = array( 'main' => $xszFfn, - 'alt' => $xszAlt, + 'alt' => $xszAlt, ); } } @@ -1223,7 +1225,7 @@ class MsDoc extends AbstractReader implements ReaderInterface /** * Paragraph and information about them - * @link http://msdn.microsoft.com/en-us/library/dd908569%28v=office.12%29.aspx + * @see http://msdn.microsoft.com/en-us/library/dd908569%28v=office.12%29.aspx */ private function readRecordPlcfBtePapx() { @@ -1247,7 +1249,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } $arrayRGB = array(); for ($inc = 1; $inc <= $numRun; $inc++) { - // @link http://msdn.microsoft.com/en-us/library/dd925804(v=office.12).aspx + // @see http://msdn.microsoft.com/en-us/library/dd925804(v=office.12).aspx $arrayRGB[$inc] = self::getInt1d($this->dataWorkDocument, $offset); $offset += 1; // reserved @@ -1303,7 +1305,7 @@ class MsDoc extends AbstractReader implements ReaderInterface print_r('$sprm.ispmd : 0x'.dechex($sprm_IsPmd).PHP_EOL); print_r('$sprm.f : 0x'.dechex($sprm_F).PHP_EOL); print_r('$sprm.sgc : 0x'.dechex($sprm_Sgc)); - switch(dechex($sprm_Sgc)) { + switch (dechex($sprm_Sgc)) { case 0x01: print_r(' (Paragraph property)'); break; @@ -1322,12 +1324,12 @@ class MsDoc extends AbstractReader implements ReaderInterface } print_r(PHP_EOL); print_r('$sprm.spra : 0x'.dechex($sprm_Spra).PHP_EOL); - switch(dechex($sprm_Spra)) { + switch (dechex($sprm_Spra)) { case 0x0: $operand = self::getInt1d($this->dataWorkDocument, $offset); $offset += 1; $cb -= 1; - switch(dechex($operand)) { + switch (dechex($operand)) { case 0x00: $operand = 'OFF'; break; @@ -1376,9 +1378,9 @@ class MsDoc extends AbstractReader implements ReaderInterface } // - switch(dechex($sprm_Sgc)) { + switch (dechex($sprm_Sgc)) { case 0x01: // Sprm is modifying a paragraph property. - switch($sprm_IsPmd) { + switch ($sprm_IsPmd) { case 0x0A: // sprmPIlvl print_r('sprmPIlvl : '.$operand.PHP_EOL.PHP_EOL); break; @@ -1391,28 +1393,28 @@ class MsDoc extends AbstractReader implements ReaderInterface } break; case 0x02: // Sprm is modifying a character property. - switch($sprm_IsPmd) { + switch ($sprm_IsPmd) { default: print_r('$sprm_IsPmd(2) : '.$sprm_IsPmd.PHP_EOL.PHP_EOL); break; } break; case 0x03: // Sprm is modifying a picture property. - switch($sprm_IsPmd) { + switch ($sprm_IsPmd) { default: print_r('$sprm_IsPmd(3) : '.$sprm_IsPmd.PHP_EOL.PHP_EOL); break; } break; case 0x04: // Sprm is modifying a section property. - switch($sprm_IsPmd) { + switch ($sprm_IsPmd) { default: print_r('$sprm_IsPmd(4) : '.$sprm_IsPmd.PHP_EOL.PHP_EOL); break; } break; case 0x05: // Sprm is modifying a table property. - switch($sprm_IsPmd) { + switch ($sprm_IsPmd) { default: print_r('$sprm_IsPmd(4) : '.$sprm_IsPmd.PHP_EOL.PHP_EOL); break; @@ -1426,7 +1428,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } else { if ($istd > 0) { // @todo : Determining Properties of a Paragraph Style - # @link http://msdn.microsoft.com/en-us/library/dd948631%28v=office.12%29.aspx + # @see http://msdn.microsoft.com/en-us/library/dd948631%28v=office.12%29.aspx } } }*/ @@ -1435,7 +1437,7 @@ class MsDoc extends AbstractReader implements ReaderInterface /** * Character formatting properties to text in a document - * @link http://msdn.microsoft.com/en-us/library/dd907108%28v=office.12%29.aspx + * @see http://msdn.microsoft.com/en-us/library/dd907108%28v=office.12%29.aspx */ private function readRecordPlcfBteChpx() { @@ -1453,7 +1455,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $offset = $offsetBase; // ChpxFkp - // @link : http://msdn.microsoft.com/en-us/library/dd910989%28v=office.12%29.aspx + // @see : http://msdn.microsoft.com/en-us/library/dd910989%28v=office.12%29.aspx $numRGFC = self::getInt1d($this->dataWorkDocument, $offset + 511); $arrayRGFC = array(); for ($inc = 0; $inc <= $numRGFC; $inc++) { @@ -1471,12 +1473,12 @@ class MsDoc extends AbstractReader implements ReaderInterface foreach ($arrayRGB as $keyRGB => $rgb) { $oStyle = new \stdClass(); $oStyle->pos_start = $start; - $oStyle->pos_len = (int)ceil((($arrayRGFC[$keyRGB] -1) - $arrayRGFC[$keyRGB -1]) / 2); + $oStyle->pos_len = (int) ceil((($arrayRGFC[$keyRGB] - 1) - $arrayRGFC[$keyRGB - 1]) / 2); $start += $oStyle->pos_len; if ($rgb > 0) { // Chp Structure - // @link : http://msdn.microsoft.com/en-us/library/dd772849%28v=office.12%29.aspx + // @see : http://msdn.microsoft.com/en-us/library/dd772849%28v=office.12%29.aspx $posRGB = $offsetBase + $rgb * 2; $cb = self::getInt1d($this->dataWorkDocument, $posRGB); @@ -1500,12 +1502,13 @@ class MsDoc extends AbstractReader implements ReaderInterface $oSprm->f = ($sprm / 512) & 0x0001; $oSprm->sgc = ($sprm / 1024) & 0x0007; $oSprm->spra = ($sprm / 8192); + return $oSprm; } /** * @param string $data - * @param integer $pos + * @param int $pos * @param \stdClass $oSprm * @return array */ @@ -1514,11 +1517,11 @@ class MsDoc extends AbstractReader implements ReaderInterface $length = 0; $operand = null; - switch(dechex($oSprm->spra)) { + switch (dechex($oSprm->spra)) { case 0x0: $operand = self::getInt1d($data, $pos); $length = 1; - switch(dechex($operand)) { + switch (dechex($operand)) { case 0x00: $operand = false; break; @@ -1558,16 +1561,17 @@ class MsDoc extends AbstractReader implements ReaderInterface } return array( - 'length' => $length, + 'length' => $length, 'operand' => $operand, ); } /** - * @param $data integer - * @param $pos integer + * @param $data int + * @param $pos int + * @param $cbNum int * @return \stdClass - * @link http://msdn.microsoft.com/en-us/library/dd772849%28v=office.12%29.aspx + * @see http://msdn.microsoft.com/en-us/library/dd772849%28v=office.12%29.aspx */ private function readPrl($data, $pos, $cbNum) { @@ -1593,7 +1597,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $cbNum -= $arrayReturn['length']; $operand = $arrayReturn['operand']; - switch(dechex($oSprm->sgc)) { + switch (dechex($oSprm->sgc)) { // Paragraph property case 0x01: break; @@ -1602,7 +1606,7 @@ class MsDoc extends AbstractReader implements ReaderInterface if (!isset($oStylePrl->styleFont)) { $oStylePrl->styleFont = array(); } - switch($oSprm->isPmd) { + switch ($oSprm->isPmd) { // sprmCFRMarkIns case 0x01: break; @@ -1620,7 +1624,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // sprmCFItalic case 0x36: // By default, text is not italicized. - switch($operand) { + switch ($operand) { case false: case true: $oStylePrl->styleFont['italic'] = $operand; @@ -1640,7 +1644,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // sprmCFBold case 0x35: // By default, text is not bold. - switch($operand) { + switch ($operand) { case false: case true: $oStylePrl->styleFont['bold'] = $operand; @@ -1656,7 +1660,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // sprmCFStrike case 0x37: // By default, text is not struck through. - switch($operand) { + switch ($operand) { case false: case true: $oStylePrl->styleFont['strikethrough'] = $operand; @@ -1671,7 +1675,7 @@ class MsDoc extends AbstractReader implements ReaderInterface break; // sprmCKul case 0x3E: - switch(dechex($operand)) { + switch (dechex($operand)) { case 0x00: $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_NONE; break; @@ -1694,7 +1698,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DASH; break; case 0x09: - $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTHASH; + $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTDASH; break; case 0x0A: $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTDOTDASH; @@ -1709,7 +1713,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DASHHEAVY; break; case 0x19: - $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTHASHHEAVY; + $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTDASHHEAVY; break; case 0x1A: $oStylePrl->styleFont['underline'] = Style\Font::UNDERLINE_DOTDOTDASHHEAVY; @@ -1732,9 +1736,9 @@ class MsDoc extends AbstractReader implements ReaderInterface } break; // sprmCIco - //@link http://msdn.microsoft.com/en-us/library/dd773060%28v=office.12%29.aspx + //@see http://msdn.microsoft.com/en-us/library/dd773060%28v=office.12%29.aspx case 0x42: - switch(dechex($operand)) { + switch (dechex($operand)) { case 0x00: case 0x01: $oStylePrl->styleFont['color'] = '000000'; @@ -1787,7 +1791,7 @@ class MsDoc extends AbstractReader implements ReaderInterface break; // sprmCHps case 0x43: - $oStylePrl->styleFont['size'] = dechex($operand/2); + $oStylePrl->styleFont['size'] = dechex($operand / 2); break; // sprmCIss case 0x48: @@ -1838,7 +1842,7 @@ class MsDoc extends AbstractReader implements ReaderInterface case 0x61: break; // sprmCShd80 - //@link http://msdn.microsoft.com/en-us/library/dd923447%28v=office.12%29.aspx + //@see http://msdn.microsoft.com/en-us/library/dd923447%28v=office.12%29.aspx case 0x66: // $operand = self::getInt2d($data, $pos); $pos += 2; @@ -1848,7 +1852,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // $icoFore = ($operand >> 11) && bindec('11111'); break; // sprmCCv - //@link : http://msdn.microsoft.com/en-us/library/dd952824%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd952824%28v=office.12%29.aspx case 0x70: $red = str_pad(dechex(self::getInt1d($this->dataWorkDocument, $pos)), 2, '0', STR_PAD_LEFT); $pos += 1; @@ -1857,7 +1861,7 @@ class MsDoc extends AbstractReader implements ReaderInterface $blue = str_pad(dechex(self::getInt1d($this->dataWorkDocument, $pos)), 2, '0', STR_PAD_LEFT); $pos += 1; $pos += 1; - $oStylePrl->styleFont['color'] = $red.$green.$blue; + $oStylePrl->styleFont['color'] = $red . $green . $blue; $cbNum -= 4; break; default: @@ -1873,7 +1877,7 @@ class MsDoc extends AbstractReader implements ReaderInterface if (!isset($oStylePrl->styleSection)) { $oStylePrl->styleSection = array(); } - switch($oSprm->isPmd) { + switch ($oSprm->isPmd) { // sprmSNfcPgn case 0x0E: // numbering format used for page numbers @@ -1925,7 +1929,6 @@ class MsDoc extends AbstractReader implements ReaderInterface default: // print_r('@todo Section : 0x'.dechex($oSprm->isPmd)); // print_r(PHP_EOL); - } break; // Table property @@ -1951,7 +1954,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // HFD > clsid $sprmCPicLocation += 16; // HFD > hyperlink - //@link : http://msdn.microsoft.com/en-us/library/dd909835%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd909835%28v=office.12%29.aspx $streamVersion = self::getInt4d($this->dataData, $sprmCPicLocation); $sprmCPicLocation += 4; $data = self::getInt4d($this->dataData, $sprmCPicLocation); @@ -2019,8 +2022,8 @@ class MsDoc extends AbstractReader implements ReaderInterface }*/ } else { // Pictures - //@link : http://msdn.microsoft.com/en-us/library/dd925458%28v=office.12%29.aspx - //@link : http://msdn.microsoft.com/en-us/library/dd926136%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd925458%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd926136%28v=office.12%29.aspx // PICF : lcb $sprmCPicLocation += 4; // PICF : cbHeader @@ -2107,13 +2110,13 @@ class MsDoc extends AbstractReader implements ReaderInterface $sprmCPicLocation += $shapeRH['recLen']; } // picture : rgfb - //@link : http://msdn.microsoft.com/en-us/library/dd950560%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd950560%28v=office.12%29.aspx $fileBlockRH = $this->loadRecordHeader($this->dataData, $sprmCPicLocation); while ($fileBlockRH['recType'] == 0xF007 || ($fileBlockRH['recType'] >= 0xF018 && $fileBlockRH['recType'] <= 0xF117)) { $sprmCPicLocation += 8; switch ($fileBlockRH['recType']) { // OfficeArtFBSE - //@link : http://msdn.microsoft.com/en-us/library/dd944923%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd944923%28v=office.12%29.aspx case 0xF007: // btWin32 $sprmCPicLocation += 1; @@ -2148,7 +2151,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } } // embeddedBlip - //@link : http://msdn.microsoft.com/en-us/library/dd910081%28v=office.12%29.aspx + //@see : http://msdn.microsoft.com/en-us/library/dd910081%28v=office.12%29.aspx $embeddedBlipRH = $this->loadRecordHeader($this->dataData, $sprmCPicLocation); switch ($embeddedBlipRH['recType']) { case self::OFFICEARTBLIPJPG: @@ -2182,6 +2185,8 @@ class MsDoc extends AbstractReader implements ReaderInterface $sprmCPicLocation += $embeddedBlipRH['recLen']; break; + case self::OFFICEARTBLIPPNG: + break; default: // print_r(dechex($embeddedBlipRH['recType'])); } @@ -2193,13 +2198,14 @@ class MsDoc extends AbstractReader implements ReaderInterface } $oStylePrl->length = $pos - $posStart; + return $oStylePrl; } /** * Read a record header * @param string $stream - * @param integer $pos + * @param int $pos * @return array */ private function loadRecordHeader($stream, $pos) @@ -2207,11 +2213,12 @@ class MsDoc extends AbstractReader implements ReaderInterface $rec = self::getInt2d($stream, $pos); $recType = self::getInt2d($stream, $pos + 2); $recLen = self::getInt4d($stream, $pos + 4); + return array( - 'recVer' => ($rec >> 0) & bindec('1111'), + 'recVer' => ($rec >> 0) & bindec('1111'), 'recInstance' => ($rec >> 4) & bindec('111111111111'), - 'recType' => $recType, - 'recLen' => $recLen, + 'recType' => $recType, + 'recLen' => $recLen, ); } @@ -2219,7 +2226,7 @@ class MsDoc extends AbstractReader implements ReaderInterface { foreach ($this->arraySections as $itmSection) { $oSection = $this->phpWord->addSection(); - $oSection->setSettings($itmSection->styleSection); + $oSection->setStyle($itmSection->styleSection); $sHYPERLINK = ''; foreach ($this->arrayParagraphs as $itmParagraph) { @@ -2274,7 +2281,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } if (ord($sText[0]) == 1) { if (isset($oCharacters->style->image)) { - $fileImage = tempnam(sys_get_temp_dir(), 'PHPWord_MsDoc').'.'.$oCharacters->style->image['format']; + $fileImage = tempnam(sys_get_temp_dir(), 'PHPWord_MsDoc') . '.' . $oCharacters->style->image['format']; file_put_contents($fileImage, $oCharacters->style->image['data']); $oSection->addImage($fileImage, array('width' => $oCharacters->style->image['width'], 'height' => $oCharacters->style->image['height'])); // print_r('>addImage<'.$fileImage.'>'.EOL); @@ -2285,7 +2292,6 @@ class MsDoc extends AbstractReader implements ReaderInterface } } } - } } @@ -2310,7 +2316,7 @@ class MsDoc extends AbstractReader implements ReaderInterface */ public static function getInt2d($data, $pos) { - return ord($data[$pos]) | (ord($data[$pos+1]) << 8); + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); } /** @@ -2322,7 +2328,7 @@ class MsDoc extends AbstractReader implements ReaderInterface */ public static function getInt3d($data, $pos) { - return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16); + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16); } /** @@ -2344,6 +2350,7 @@ class MsDoc extends AbstractReader implements ReaderInterface } else { $ord24 = ($or24 & 127) << 24; } - return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $ord24; + + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $ord24; } } diff --git a/src/PhpWord/Reader/ODText.php b/src/PhpWord/Reader/ODText.php index e8c86886..0b58dc50 100644 --- a/src/PhpWord/Reader/ODText.php +++ b/src/PhpWord/Reader/ODText.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -40,7 +40,7 @@ class ODText extends AbstractReader implements ReaderInterface $readerParts = array( 'content.xml' => 'Content', - 'meta.xml' => 'Meta', + 'meta.xml' => 'Meta', ); foreach ($readerParts as $xmlFile => $partName) { @@ -58,7 +58,6 @@ class ODText extends AbstractReader implements ReaderInterface * @param string $partName * @param string $docFile * @param string $xmlFile - * @return void */ private function readPart(PhpWord $phpWord, $relationships, $partName, $docFile, $xmlFile) { diff --git a/src/PhpWord/Reader/ODText/AbstractPart.php b/src/PhpWord/Reader/ODText/AbstractPart.php index 5ec2f22d..ff664e01 100644 --- a/src/PhpWord/Reader/ODText/AbstractPart.php +++ b/src/PhpWord/Reader/ODText/AbstractPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Reader/ODText/Content.php b/src/PhpWord/Reader/ODText/Content.php index 7362b02c..9dfd6453 100644 --- a/src/PhpWord/Reader/ODText/Content.php +++ b/src/PhpWord/Reader/ODText/Content.php @@ -10,14 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Reader\ODText; use PhpOffice\Common\XMLReader; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; /** @@ -31,29 +32,57 @@ class Content extends AbstractPart * Read content.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); + $trackedChanges = array(); + $nodes = $xmlReader->getElements('office:body/office:text/*'); if ($nodes->length > 0) { $section = $phpWord->addSection(); foreach ($nodes as $node) { // $styleName = $xmlReader->getAttribute('text:style-name', $node); switch ($node->nodeName) { - case 'text:h': // Heading $depth = $xmlReader->getAttribute('text:outline-level', $node); $section->addTitle($node->nodeValue, $depth); break; - case 'text:p': // Paragraph - $section->addText($node->nodeValue); - break; + $children = $node->childNodes; + foreach ($children as $child) { + switch ($child->nodeName) { + case 'text:change-start': + $changeId = $child->getAttribute('text:change-id'); + if (isset($trackedChanges[$changeId])) { + $changed = $trackedChanges[$changeId]; + } + break; + case 'text:change-end': + unset($changed); + break; + case 'text:change': + $changeId = $child->getAttribute('text:change-id'); + if (isset($trackedChanges[$changeId])) { + $changed = $trackedChanges[$changeId]; + } + break; + } + } + $element = $section->addText($node->nodeValue); + if (isset($changed) && is_array($changed)) { + $element->setTrackChange($changed['changed']); + if (isset($changed['textNodes'])) { + foreach ($changed['textNodes'] as $changedNode) { + $element = $section->addText($changedNode->nodeValue); + $element->setTrackChange($changed['changed']); + } + } + } + break; case 'text:list': // List $listItems = $xmlReader->getElements('text:list-item/text:p', $node); foreach ($listItems as $listItem) { @@ -61,6 +90,21 @@ class Content extends AbstractPart $section->addListItem($listItem->nodeValue, 0); } break; + case 'text:tracked-changes': + $changedRegions = $xmlReader->getElements('text:changed-region', $node); + foreach ($changedRegions as $changedRegion) { + $type = ($changedRegion->firstChild->nodeName == 'text:insertion') ? TrackChange::INSERTED : TrackChange::DELETED; + $creatorNode = $xmlReader->getElements('office:change-info/dc:creator', $changedRegion->firstChild); + $author = $creatorNode[0]->nodeValue; + $dateNode = $xmlReader->getElements('office:change-info/dc:date', $changedRegion->firstChild); + $date = $dateNode[0]->nodeValue; + $date = preg_replace('/\.\d+$/', '', $date); + $date = \DateTime::createFromFormat('Y-m-d\TH:i:s', $date); + $changed = new TrackChange($type, $author, $date); + $textNodes = $xmlReader->getElements('text:deletion/text:p', $changedRegion); + $trackedChanges[$changedRegion->getAttribute('text:id')] = array('changed' => $changed, 'textNodes'=> $textNodes); + } + break; } } } diff --git a/src/PhpWord/Reader/ODText/Meta.php b/src/PhpWord/Reader/ODText/Meta.php index 9533c38b..8801a543 100644 --- a/src/PhpWord/Reader/ODText/Meta.php +++ b/src/PhpWord/Reader/ODText/Meta.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -31,7 +31,6 @@ class Meta extends AbstractPart * Read meta.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void * @todo Process property type */ public function read(PhpWord $phpWord) @@ -70,9 +69,8 @@ class Meta extends AbstractPart if (in_array($property, array('Category', 'Company', 'Manager'))) { $method = "set{$property}"; $docProps->$method($propertyNode->nodeValue); - - // Set other custom properties } else { + // Set other custom properties $docProps->setCustomProperty($property, $propertyNode->nodeValue); } } diff --git a/src/PhpWord/Reader/RTF.php b/src/PhpWord/Reader/RTF.php index b6d2e21e..620252ff 100644 --- a/src/PhpWord/Reader/RTF.php +++ b/src/PhpWord/Reader/RTF.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Reader/RTF/Document.php b/src/PhpWord/Reader/RTF/Document.php index 4e0f2c35..b9509d71 100644 --- a/src/PhpWord/Reader/RTF/Document.php +++ b/src/PhpWord/Reader/RTF/Document.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -131,7 +131,6 @@ class Document * - Pushes every other character into the text queue * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void * @todo Use `fread` stream for scalability */ public function read(PhpWord $phpWord) @@ -153,7 +152,7 @@ class Document // Walk each characters while ($this->offset < $this->length) { - $char = $this->rtf[$this->offset]; + $char = $this->rtf[$this->offset]; $ascii = ord($char); if (isset($markers[$ascii])) { // Marker found: {, }, \, LF, or CR @@ -163,7 +162,7 @@ class Document if (false === $this->isControl) { // Non control word: Push character $this->pushText($char); } else { - if (preg_match("/^[a-zA-Z0-9-]?$/", $char)) { // No delimiter: Buffer control + if (preg_match('/^[a-zA-Z0-9-]?$/', $char)) { // No delimiter: Buffer control $this->control .= $char; $this->isFirst = false; } else { // Delimiter found: Parse buffered control @@ -184,8 +183,6 @@ class Document /** * Mark opening braket `{` character. - * - * @return void */ private function markOpening() { @@ -195,8 +192,6 @@ class Document /** * Mark closing braket `}` character. - * - * @return void */ private function markClosing() { @@ -206,8 +201,6 @@ class Document /** * Mark backslash `\` character. - * - * @return void */ private function markBackslash() { @@ -223,8 +216,6 @@ class Document /** * Mark newline character: Flush control word because it's not possible to span multiline. - * - * @return void */ private function markNewline() { @@ -237,7 +228,6 @@ class Document * Flush control word or text. * * @param bool $isControl - * @return void */ private function flush($isControl = false) { @@ -252,11 +242,10 @@ class Document * Flush control word. * * @param bool $isControl - * @return void */ private function flushControl($isControl = false) { - if (1 === preg_match("/^([A-Za-z]+)(-?[0-9]*) ?$/", $this->control, $match)) { + if (1 === preg_match('/^([A-Za-z]+)(-?[0-9]*) ?$/', $this->control, $match)) { list(, $control, $parameter) = $match; $this->parseControl($control, $parameter); } @@ -268,8 +257,6 @@ class Document /** * Flush text in queue. - * - * @return void */ private function flushText() { @@ -296,7 +283,6 @@ class Document * Reset control word and first char state. * * @param bool $value - * @return void */ private function setControl($value) { @@ -308,14 +294,13 @@ class Document * Push text into queue. * * @param string $char - * @return void */ private function pushText($char) { if ('<' == $char) { - $this->text .= "<"; + $this->text .= '<'; } elseif ('>' == $char) { - $this->text .= ">"; + $this->text .= '>'; } else { $this->text .= $char; } @@ -326,19 +311,18 @@ class Document * * @param string $control * @param string $parameter - * @return void */ private function parseControl($control, $parameter) { $controls = array( 'par' => array(self::PARA, 'paragraph', true), - 'b' => array(self::STYL, 'font', 'bold', true), - 'i' => array(self::STYL, 'font', 'italic', true), - 'u' => array(self::STYL, 'font', 'underline', true), - 'strike' => array(self::STYL, 'font', 'strikethrough',true), - 'fs' => array(self::STYL, 'font', 'size', $parameter), - 'qc' => array(self::STYL, 'paragraph', 'alignment', Jc::CENTER), - 'sa' => array(self::STYL, 'paragraph', 'spaceAfter', $parameter), + 'b' => array(self::STYL, 'font', 'bold', true), + 'i' => array(self::STYL, 'font', 'italic', true), + 'u' => array(self::STYL, 'font', 'underline', true), + 'strike' => array(self::STYL, 'font', 'strikethrough', true), + 'fs' => array(self::STYL, 'font', 'size', $parameter), + 'qc' => array(self::STYL, 'paragraph', 'alignment', Jc::CENTER), + 'sa' => array(self::STYL, 'paragraph', 'spaceAfter', $parameter), 'fonttbl' => array(self::SKIP, 'fonttbl', null), 'colortbl' => array(self::SKIP, 'colortbl', null), 'info' => array(self::SKIP, 'info', null), @@ -366,7 +350,6 @@ class Document * Read paragraph. * * @param array $directives - * @return void */ private function readParagraph($directives) { @@ -379,7 +362,6 @@ class Document * Read style. * * @param array $directives - * @return void */ private function readStyle($directives) { @@ -391,7 +373,6 @@ class Document * Read skip. * * @param array $directives - * @return void */ private function readSkip($directives) { @@ -402,8 +383,6 @@ class Document /** * Read text. - * - * @return void */ private function readText() { diff --git a/src/PhpWord/Reader/ReaderInterface.php b/src/PhpWord/Reader/ReaderInterface.php index 4f5231a7..4024cdb3 100644 --- a/src/PhpWord/Reader/ReaderInterface.php +++ b/src/PhpWord/Reader/ReaderInterface.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,7 +28,7 @@ interface ReaderInterface * Can the current ReaderInterface read the file? * * @param string $filename - * @return boolean + * @return bool */ public function canRead($filename); diff --git a/src/PhpWord/Reader/Word2007.php b/src/PhpWord/Reader/Word2007.php index da20eb87..52030ef8 100644 --- a/src/PhpWord/Reader/Word2007.php +++ b/src/PhpWord/Reader/Word2007.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -55,12 +55,16 @@ class Word2007 extends AbstractReader implements ReaderInterface array('stepPart' => 'document', 'stepItems' => array( 'endnotes' => 'Endnotes', 'footnotes' => 'Footnotes', + 'settings' => 'Settings', )), ); foreach ($steps as $step) { $stepPart = $step['stepPart']; $stepItems = $step['stepItems']; + if (!isset($relationships[$stepPart])) { + continue; + } foreach ($relationships[$stepPart] as $relItem) { $relType = $relItem['type']; if (isset($stepItems[$relType])) { @@ -82,7 +86,6 @@ class Word2007 extends AbstractReader implements ReaderInterface * @param string $partName * @param string $docFile * @param string $xmlFile - * @return void */ private function readPart(PhpWord $phpWord, $relationships, $partName, $docFile, $xmlFile) { @@ -93,7 +96,6 @@ class Word2007 extends AbstractReader implements ReaderInterface $part->setRels($relationships); $part->read($phpWord); } - } /** @@ -148,6 +150,7 @@ class Word2007 extends AbstractReader implements ReaderInterface $rId = $xmlReader->getAttribute('Id', $node); $type = $xmlReader->getAttribute('Type', $node); $target = $xmlReader->getAttribute('Target', $node); + $mode = $xmlReader->getAttribute('TargetMode', $node); // Remove URL prefixes from $type to make it easier to read $type = str_replace($metaPrefix, '', $type); @@ -155,12 +158,12 @@ class Word2007 extends AbstractReader implements ReaderInterface $docPart = str_replace('.xml', '', $target); // Do not add prefix to link source - if (!in_array($type, array('hyperlink'))) { + if ($type != 'hyperlink' && $mode != 'External') { $target = $targetPrefix . $target; } // Push to return array - $rels[$rId] = array('type' => $type, 'target' => $target, 'docPart' => $docPart); + $rels[$rId] = array('type' => $type, 'target' => $target, 'docPart' => $docPart, 'targetMode' => $mode); } ksort($rels); diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index f429ff75..5e5eb1d6 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -10,14 +10,18 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Reader\Word2007; use PhpOffice\Common\XMLReader; +use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType; +use PhpOffice\PhpWord\Element\AbstractContainer; +use PhpOffice\PhpWord\Element\TextRun; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; /** @@ -36,9 +40,9 @@ abstract class AbstractPart */ const READ_VALUE = 'attributeValue'; // Read attribute value const READ_EQUAL = 'attributeEquals'; // Read `true` when attribute value equals specified value - const READ_TRUE = 'attributeTrue'; // Read `true` when element exists + const READ_TRUE = 'attributeTrue'; // Read `true` when element exists const READ_FALSE = 'attributeFalse'; // Read `false` when element exists - const READ_SIZE = 'attributeMultiplyByTwo'; // Read special attribute value for Font::$size + const READ_SIZE = 'attributeMultiplyByTwo'; // Read special attribute value for Font::$size /** * Document file @@ -82,7 +86,6 @@ abstract class AbstractPart * Set relationships. * * @param array $value - * @return void */ public function setRels($value) { @@ -94,9 +97,8 @@ abstract class AbstractPart * * @param \PhpOffice\Common\XMLReader $xmlReader * @param \DOMElement $domNode - * @param mixed $parent + * @param \PhpOffice\PhpWord\Element\AbstractContainer $parent * @param string $docPart - * @return void * * @todo Get font style for preserve text */ @@ -104,12 +106,10 @@ abstract class AbstractPart { // Paragraph style $paragraphStyle = null; - $headingMatches = array(); + $headingDepth = null; if ($xmlReader->elementExists('w:pPr', $domNode)) { $paragraphStyle = $this->readParagraphStyle($xmlReader, $domNode); - if (is_array($paragraphStyle) && isset($paragraphStyle['styleName'])) { - preg_match('/Heading(\d)/', $paragraphStyle['styleName'], $headingMatches); - } + $headingDepth = $this->getHeadingDepth($paragraphStyle); } // PreserveText @@ -136,109 +136,177 @@ abstract class AbstractPart } } } - $parent->addPreserveText($textContent, $fontStyle, $paragraphStyle); - - // List item + $parent->addPreserveText(htmlspecialchars($textContent, ENT_QUOTES, 'UTF-8'), $fontStyle, $paragraphStyle); } elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) { - $textContent = ''; + // List item $numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId'); $levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl'); - $nodes = $xmlReader->getElements('w:r', $domNode); - foreach ($nodes as $node) { - $textContent .= $xmlReader->getValue('w:t', $node); - } - $parent->addListItem($textContent, $levelId, null, "PHPWordList{$numId}", $paragraphStyle); + $nodes = $xmlReader->getElements('*', $domNode); - // Heading - } elseif (!empty($headingMatches)) { - $textContent = ''; - $nodes = $xmlReader->getElements('w:r', $domNode); - foreach ($nodes as $node) { - $textContent .= $xmlReader->getValue('w:t', $node); - } - $parent->addTitle($textContent, $headingMatches[1]); + $listItemRun = $parent->addListItemRun($levelId, "PHPWordList{$numId}", $paragraphStyle); - // Text and TextRun + foreach ($nodes as $node) { + $this->readRun($xmlReader, $node, $listItemRun, $docPart, $paragraphStyle); + } + } elseif ($headingDepth !== null) { + // Heading or Title + $textContent = null; + $nodes = $xmlReader->getElements('w:r', $domNode); + if ($nodes->length === 1) { + $textContent = htmlspecialchars($xmlReader->getValue('w:t', $nodes->item(0)), ENT_QUOTES, 'UTF-8'); + } else { + $textContent = new TextRun($paragraphStyle); + foreach ($nodes as $node) { + $this->readRun($xmlReader, $node, $textContent, $docPart, $paragraphStyle); + } + } + $parent->addTitle($textContent, $headingDepth); } else { - $runCount = $xmlReader->countElements('w:r', $domNode); - $linkCount = $xmlReader->countElements('w:hyperlink', $domNode); - $runLinkCount = $runCount + $linkCount; - if (0 == $runLinkCount) { + // Text and TextRun + $textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag', $domNode); + if (0 === $textRunContainers) { $parent->addTextBreak(null, $paragraphStyle); } else { $nodes = $xmlReader->getElements('*', $domNode); + $paragraph = $parent->addTextRun($paragraphStyle); foreach ($nodes as $node) { - $this->readRun( - $xmlReader, - $node, - ($runLinkCount > 1) ? $parent->addTextRun($paragraphStyle) : $parent, - $docPart, - $paragraphStyle - ); + $this->readRun($xmlReader, $node, $paragraph, $docPart, $paragraphStyle); } } } } + /** + * Returns the depth of the Heading, returns 0 for a Title + * + * @param array $paragraphStyle + * @return number|null + */ + private function getHeadingDepth(array $paragraphStyle = null) + { + if (is_array($paragraphStyle) && isset($paragraphStyle['styleName'])) { + if ('Title' === $paragraphStyle['styleName']) { + return 0; + } + + $headingMatches = array(); + preg_match('/Heading(\d)/', $paragraphStyle['styleName'], $headingMatches); + if (!empty($headingMatches)) { + return $headingMatches[1]; + } + } + + return null; + } + /** * Read w:r. * * @param \PhpOffice\Common\XMLReader $xmlReader * @param \DOMElement $domNode - * @param mixed $parent + * @param \PhpOffice\PhpWord\Element\AbstractContainer $parent * @param string $docPart * @param mixed $paragraphStyle - * @return void * * @todo Footnote paragraph style */ protected function readRun(XMLReader $xmlReader, \DOMElement $domNode, $parent, $docPart, $paragraphStyle = null) { - if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) { - return; + if (in_array($domNode->nodeName, array('w:ins', 'w:del', 'w:smartTag', 'w:hyperlink'))) { + $nodes = $xmlReader->getElements('*', $domNode); + foreach ($nodes as $node) { + $this->readRun($xmlReader, $node, $parent, $docPart, $paragraphStyle); + } + } elseif ($domNode->nodeName == 'w:r') { + $fontStyle = $this->readFontStyle($xmlReader, $domNode); + $nodes = $xmlReader->getElements('*', $domNode); + foreach ($nodes as $node) { + $this->readRunChild($xmlReader, $node, $parent, $docPart, $paragraphStyle, $fontStyle); + } } - $fontStyle = $this->readFontStyle($xmlReader, $domNode); + } - // Link - if ('w:hyperlink' == $domNode->nodeName) { - $rId = $xmlReader->getAttribute('r:id', $domNode); - $textContent = $xmlReader->getValue('w:r/w:t', $domNode); + /** + * Parses nodes under w:r + * + * @param XMLReader $xmlReader + * @param \DOMElement $node + * @param AbstractContainer $parent + * @param string $docPart + * @param mixed $paragraphStyle + * @param mixed $fontStyle + */ + protected function readRunChild(XMLReader $xmlReader, \DOMElement $node, AbstractContainer $parent, $docPart, $paragraphStyle = null, $fontStyle = null) + { + $runParent = $node->parentNode->parentNode; + if ($node->nodeName == 'w:footnoteReference') { + // Footnote + $wId = $xmlReader->getAttribute('w:id', $node); + $footnote = $parent->addFootnote(); + $footnote->setRelationId($wId); + } elseif ($node->nodeName == 'w:endnoteReference') { + // Endnote + $wId = $xmlReader->getAttribute('w:id', $node); + $endnote = $parent->addEndnote(); + $endnote->setRelationId($wId); + } elseif ($node->nodeName == 'w:pict') { + // Image + $rId = $xmlReader->getAttribute('r:id', $node, 'v:shape/v:imagedata'); $target = $this->getMediaTarget($docPart, $rId); if (!is_null($target)) { - $parent->addLink($target, $textContent, $fontStyle, $paragraphStyle); - } - } else { - // Footnote - if ($xmlReader->elementExists('w:footnoteReference', $domNode)) { - $parent->addFootnote(); - - // Endnote - } elseif ($xmlReader->elementExists('w:endnoteReference', $domNode)) { - $parent->addEndnote(); - - // Image - } elseif ($xmlReader->elementExists('w:pict', $domNode)) { - $rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata'); - $target = $this->getMediaTarget($docPart, $rId); - if (!is_null($target)) { + if ('External' == $this->getTargetMode($docPart, $rId)) { + $imageSource = $target; + } else { $imageSource = "zip://{$this->docFile}#{$target}"; - $parent->addImage($imageSource); } + $parent->addImage($imageSource); + } + } elseif ($node->nodeName == 'w:drawing') { + // Office 2011 Image + $xmlReader->registerNamespace('wp', 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing'); + $xmlReader->registerNamespace('r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $xmlReader->registerNamespace('pic', 'http://schemas.openxmlformats.org/drawingml/2006/picture'); + $xmlReader->registerNamespace('a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + $name = $xmlReader->getAttribute('name', $node, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:nvPicPr/pic:cNvPr'); + $embedId = $xmlReader->getAttribute('r:embed', $node, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip'); + $target = $this->getMediaTarget($docPart, $embedId); + if (!is_null($target)) { + $imageSource = "zip://{$this->docFile}#{$target}"; + $parent->addImage($imageSource, null, false, $name); + } + } elseif ($node->nodeName == 'w:object') { // Object - } elseif ($xmlReader->elementExists('w:object', $domNode)) { - $rId = $xmlReader->getAttribute('r:id', $domNode, 'w:object/o:OLEObject'); - // $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata'); + $rId = $xmlReader->getAttribute('r:id', $node, 'o:OLEObject'); + // $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata'); + $target = $this->getMediaTarget($docPart, $rId); + if (!is_null($target)) { + $textContent = "<Object: {$target}>"; + $parent->addText($textContent, $fontStyle, $paragraphStyle); + } + } elseif ($node->nodeName == 'w:br') { + $parent->addTextBreak(); + } elseif ($node->nodeName == 'w:tab') { + $parent->addText("\t"); + } elseif ($node->nodeName == 'w:t' || $node->nodeName == 'w:delText') { + // TextRun + $textContent = htmlspecialchars($xmlReader->getValue('.', $node), ENT_QUOTES, 'UTF-8'); + + if ($runParent->nodeName == 'w:hyperlink') { + $rId = $xmlReader->getAttribute('r:id', $runParent); $target = $this->getMediaTarget($docPart, $rId); if (!is_null($target)) { - $textContent = ""; - $parent->addText($textContent, $fontStyle, $paragraphStyle); + $parent->addLink($target, $textContent, $fontStyle, $paragraphStyle); } - - // TextRun } else { - $textContent = $xmlReader->getValue('w:t', $domNode); - $parent->addText($textContent, $fontStyle, $paragraphStyle); + /** @var AbstractElement $element */ + $element = $parent->addText($textContent, $fontStyle, $paragraphStyle); + if (in_array($runParent->nodeName, array('w:ins', 'w:del'))) { + $type = ($runParent->nodeName == 'w:del') ? TrackChange::DELETED : TrackChange::INSERTED; + $author = $runParent->getAttribute('w:author'); + $date = \DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $runParent->getAttribute('w:date')); + $element->setChangeInfo($type, $author, $date); + } } } } @@ -250,7 +318,6 @@ abstract class AbstractPart * @param \DOMElement $domNode * @param mixed $parent * @param string $docPart - * @return void */ protected function readTable(XMLReader $xmlReader, \DOMElement $domNode, $parent, $docPart = 'document') { @@ -266,7 +333,6 @@ abstract class AbstractPart foreach ($tblNodes as $tblNode) { if ('w:tblGrid' == $tblNode->nodeName) { // Column // @todo Do something with table columns - } elseif ('w:tr' == $tblNode->nodeName) { // Row $rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight'); $rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight'); @@ -282,7 +348,6 @@ abstract class AbstractPart foreach ($rowNodes as $rowNode) { if ('w:trPr' == $rowNode->nodeName) { // Row style // @todo Do something with row style - } elseif ('w:tc' == $rowNode->nodeName) { // Cell $cellWidth = $xmlReader->getAttribute('w:w', $rowNode, 'w:tcPr/w:tcW'); $cellStyle = null; @@ -319,18 +384,21 @@ abstract class AbstractPart $styleNode = $xmlReader->getElement('w:pPr', $domNode); $styleDefs = array( - 'styleName' => array(self::READ_VALUE, 'w:pStyle'), - 'alignment' => array(self::READ_VALUE, 'w:jc'), - 'basedOn' => array(self::READ_VALUE, 'w:basedOn'), - 'next' => array(self::READ_VALUE, 'w:next'), - 'indent' => array(self::READ_VALUE, 'w:ind', 'w:left'), - 'hanging' => array(self::READ_VALUE, 'w:ind', 'w:hanging'), - 'spaceAfter' => array(self::READ_VALUE, 'w:spacing', 'w:after'), - 'spaceBefore' => array(self::READ_VALUE, 'w:spacing', 'w:before'), - 'widowControl' => array(self::READ_FALSE, 'w:widowControl'), - 'keepNext' => array(self::READ_TRUE, 'w:keepNext'), - 'keepLines' => array(self::READ_TRUE, 'w:keepLines'), - 'pageBreakBefore' => array(self::READ_TRUE, 'w:pageBreakBefore'), + 'styleName' => array(self::READ_VALUE, array('w:pStyle', 'w:name')), + 'alignment' => array(self::READ_VALUE, 'w:jc'), + 'basedOn' => array(self::READ_VALUE, 'w:basedOn'), + 'next' => array(self::READ_VALUE, 'w:next'), + 'indent' => array(self::READ_VALUE, 'w:ind', 'w:left'), + 'hanging' => array(self::READ_VALUE, 'w:ind', 'w:hanging'), + 'spaceAfter' => array(self::READ_VALUE, 'w:spacing', 'w:after'), + 'spaceBefore' => array(self::READ_VALUE, 'w:spacing', 'w:before'), + 'widowControl' => array(self::READ_FALSE, 'w:widowControl'), + 'keepNext' => array(self::READ_TRUE, 'w:keepNext'), + 'keepLines' => array(self::READ_TRUE, 'w:keepLines'), + 'pageBreakBefore' => array(self::READ_TRUE, 'w:pageBreakBefore'), + 'contextualSpacing' => array(self::READ_TRUE, 'w:contextualSpacing'), + 'bidi' => array(self::READ_TRUE, 'w:bidi'), + 'suppressAutoHyphens' => array(self::READ_TRUE, 'w:suppressAutoHyphens'), ); return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); @@ -359,9 +427,9 @@ abstract class AbstractPart $styleNode = $xmlReader->getElement('w:rPr', $domNode); $styleDefs = array( 'styleName' => array(self::READ_VALUE, 'w:rStyle'), - 'name' => array(self::READ_VALUE, 'w:rFonts', 'w:ascii'), + 'name' => array(self::READ_VALUE, 'w:rFonts', array('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs')), 'hint' => array(self::READ_VALUE, 'w:rFonts', 'w:hint'), - 'size' => array(self::READ_SIZE, 'w:sz'), + 'size' => array(self::READ_SIZE, array('w:sz', 'w:szCs')), 'color' => array(self::READ_VALUE, 'w:color'), 'underline' => array(self::READ_VALUE, 'w:u'), 'bold' => array(self::READ_TRUE, 'w:b'), @@ -374,6 +442,8 @@ abstract class AbstractPart 'subScript' => array(self::READ_EQUAL, 'w:vertAlign', 'w:val', 'subscript'), 'fgColor' => array(self::READ_VALUE, 'w:highlight'), 'rtl' => array(self::READ_TRUE, 'w:rtl'), + 'lang' => array(self::READ_VALUE, 'w:lang'), + 'position' => array(self::READ_VALUE, 'w:position'), ); return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); @@ -391,7 +461,7 @@ abstract class AbstractPart { $style = null; $margins = array('top', 'left', 'bottom', 'right'); - $borders = $margins + array('insideH', 'insideV'); + $borders = array_merge($margins, array('insideH', 'insideV')); if ($xmlReader->elementExists('w:tblPr', $domNode)) { if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) { @@ -407,14 +477,70 @@ abstract class AbstractPart $ucfSide = ucfirst($side); $styleDefs["border{$ucfSide}Size"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz'); $styleDefs["border{$ucfSide}Color"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:color'); + $styleDefs["border{$ucfSide}Style"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:val'); } + $styleDefs['layout'] = array(self::READ_VALUE, 'w:tblLayout', 'w:type'); + $styleDefs['cellSpacing'] = array(self::READ_VALUE, 'w:tblCellSpacing', 'w:w'); $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); + + $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode); + if ($tablePositionNode !== null) { + $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode); + } + + $indentNode = $xmlReader->getElement('w:tblInd', $styleNode); + if ($indentNode !== null) { + $style['indent'] = $this->readTableIndent($xmlReader, $indentNode); + } } } return $style; } + /** + * Read w:tblpPr + * + * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \DOMElement $domNode + * @return array + */ + private function readTablePosition(XMLReader $xmlReader, \DOMElement $domNode) + { + $styleDefs = array( + 'leftFromText' => array(self::READ_VALUE, '.', 'w:leftFromText'), + 'rightFromText' => array(self::READ_VALUE, '.', 'w:rightFromText'), + 'topFromText' => array(self::READ_VALUE, '.', 'w:topFromText'), + 'bottomFromText' => array(self::READ_VALUE, '.', 'w:bottomFromText'), + 'vertAnchor' => array(self::READ_VALUE, '.', 'w:vertAnchor'), + 'horzAnchor' => array(self::READ_VALUE, '.', 'w:horzAnchor'), + 'tblpXSpec' => array(self::READ_VALUE, '.', 'w:tblpXSpec'), + 'tblpX' => array(self::READ_VALUE, '.', 'w:tblpX'), + 'tblpYSpec' => array(self::READ_VALUE, '.', 'w:tblpYSpec'), + 'tblpY' => array(self::READ_VALUE, '.', 'w:tblpY'), + ); + + return $this->readStyleDefs($xmlReader, $domNode, $styleDefs); + } + + /** + * Read w:tblInd + * + * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \DOMElement $domNode + * @return TblWidthComplexType + */ + private function readTableIndent(XMLReader $xmlReader, \DOMElement $domNode) + { + $styleDefs = array( + 'value' => array(self::READ_VALUE, '.', 'w:w'), + 'type' => array(self::READ_VALUE, '.', 'w:type'), + ); + $styleDefs = $this->readStyleDefs($xmlReader, $domNode, $styleDefs); + + return new TblWidthComplexType((int) $styleDefs['value'], $styleDefs['type']); + } + /** * Read w:tcPr * @@ -429,12 +555,60 @@ abstract class AbstractPart 'textDirection' => array(self::READ_VALUE, 'w:textDirection'), 'gridSpan' => array(self::READ_VALUE, 'w:gridSpan'), 'vMerge' => array(self::READ_VALUE, 'w:vMerge'), - 'bgColor' => array(self::READ_VALUE, 'w:shd/w:fill'), + 'bgColor' => array(self::READ_VALUE, 'w:shd', 'w:fill'), ); return $this->readStyleDefs($xmlReader, $domNode, $styleDefs); } + /** + * Returns the first child element found + * + * @param XMLReader $xmlReader + * @param \DOMElement $parentNode + * @param string|array $elements + * @return string|null + */ + private function findPossibleElement(XMLReader $xmlReader, \DOMElement $parentNode = null, $elements) + { + if (is_array($elements)) { + //if element is an array, we take the first element that exists in the XML + foreach ($elements as $possibleElement) { + if ($xmlReader->elementExists($possibleElement, $parentNode)) { + return $possibleElement; + } + } + } else { + return $elements; + } + + return null; + } + + /** + * Returns the first attribute found + * + * @param XMLReader $xmlReader + * @param \DOMElement $node + * @param string|array $attributes + * @return string|null + */ + private function findPossibleAttribute(XMLReader $xmlReader, \DOMElement $node, $attributes) + { + //if attribute is an array, we take the first attribute that exists in the XML + if (is_array($attributes)) { + foreach ($attributes as $possibleAttribute) { + if ($xmlReader->getAttribute($possibleAttribute, $node)) { + return $possibleAttribute; + } + } + + return null; + } + + return $attributes; + } + /** * Read style definition * @@ -449,11 +623,18 @@ abstract class AbstractPart $styles = array(); foreach ($styleDefs as $styleProp => $styleVal) { - @list($method, $element, $attribute, $expected) = $styleVal; + list($method, $element, $attribute, $expected) = array_pad($styleVal, 4, null); + + $element = $this->findPossibleElement($xmlReader, $parentNode, $element); + if ($element === null) { + continue; + } if ($xmlReader->elementExists($element, $parentNode)) { $node = $xmlReader->getElement($element, $parentNode); + $attribute = $this->findPossibleAttribute($xmlReader, $node, $attribute); + // Use w:val as default if no attribute assigned $attribute = ($attribute === null) ? 'w:val' : $attribute; $attributeValue = $xmlReader->getAttribute($attribute, $node); @@ -473,7 +654,7 @@ abstract class AbstractPart * * @param string $method * @ignoreScrutinizerPatch - * @param mixed $attributeValue + * @param string|null $attributeValue * @param mixed $expected * @return mixed */ @@ -484,9 +665,9 @@ abstract class AbstractPart if (self::READ_SIZE == $method) { $style = $attributeValue / 2; } elseif (self::READ_TRUE == $method) { - $style = true; + $style = $this->isOn($attributeValue); } elseif (self::READ_FALSE == $method) { - $style = false; + $style = !$this->isOn($attributeValue); } elseif (self::READ_EQUAL == $method) { $style = $attributeValue == $expected; } @@ -494,6 +675,18 @@ abstract class AbstractPart return $style; } + /** + * Parses the value of the on/off value, null is considered true as it means the w:val attribute was not present + * + * @see http://www.datypic.com/sc/ooxml/t-w_ST_OnOff.html + * @param string $value + * @return bool + */ + private function isOn($value = null) + { + return $value === null || $value === '1' || $value === 'true' || $value === 'on'; + } + /** * Returns the target of image, object, or link as stored in ::readMainRels * @@ -511,4 +704,22 @@ abstract class AbstractPart return $target; } + + /** + * Returns the target mode + * + * @param string $docPart + * @param string $rId + * @return string|null + */ + private function getTargetMode($docPart, $rId) + { + $mode = null; + + if (isset($this->rels[$docPart]) && isset($this->rels[$docPart][$rId])) { + $mode = $this->rels[$docPart][$rId]['targetMode']; + } + + return $mode; + } } diff --git a/src/PhpWord/Reader/Word2007/DocPropsApp.php b/src/PhpWord/Reader/Word2007/DocPropsApp.php index 6b1410a5..decc5103 100644 --- a/src/PhpWord/Reader/Word2007/DocPropsApp.php +++ b/src/PhpWord/Reader/Word2007/DocPropsApp.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Reader/Word2007/DocPropsCore.php b/src/PhpWord/Reader/Word2007/DocPropsCore.php index 417a93bd..36eecebe 100644 --- a/src/PhpWord/Reader/Word2007/DocPropsCore.php +++ b/src/PhpWord/Reader/Word2007/DocPropsCore.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,15 +33,15 @@ class DocPropsCore extends AbstractPart * @var array */ protected $mapping = array( - 'dc:creator' => 'setCreator', - 'dc:title' => 'setTitle', - 'dc:description' => 'setDescription', - 'dc:subject' => 'setSubject', - 'cp:keywords' => 'setKeywords', - 'cp:category' => 'setCategory', + 'dc:creator' => 'setCreator', + 'dc:title' => 'setTitle', + 'dc:description' => 'setDescription', + 'dc:subject' => 'setSubject', + 'cp:keywords' => 'setKeywords', + 'cp:category' => 'setCategory', 'cp:lastModifiedBy' => 'setLastModifiedBy', - 'dcterms:created' => 'setCreated', - 'dcterms:modified' => 'setModified', + 'dcterms:created' => 'setCreated', + 'dcterms:modified' => 'setModified', ); /** @@ -55,7 +55,6 @@ class DocPropsCore extends AbstractPart * Read core/extended document properties. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { diff --git a/src/PhpWord/Reader/Word2007/DocPropsCustom.php b/src/PhpWord/Reader/Word2007/DocPropsCustom.php index 979a2441..a6835aac 100644 --- a/src/PhpWord/Reader/Word2007/DocPropsCustom.php +++ b/src/PhpWord/Reader/Word2007/DocPropsCustom.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -32,7 +32,6 @@ class DocPropsCustom extends AbstractPart * Read custom document properties. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { diff --git a/src/PhpWord/Reader/Word2007/Document.php b/src/PhpWord/Reader/Word2007/Document.php index b89a99ad..4e37541b 100644 --- a/src/PhpWord/Reader/Word2007/Document.php +++ b/src/PhpWord/Reader/Word2007/Document.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -40,7 +40,6 @@ class Document extends AbstractPart * Read document.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { @@ -66,7 +65,6 @@ class Document extends AbstractPart * * @param array $settings * @param \PhpOffice\PhpWord\Element\Section &$section - * @return void */ private function readHeaderFooter($settings, Section &$section) { @@ -113,10 +111,10 @@ class Document extends AbstractPart 'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'), 'colsNum' => array(self::READ_VALUE, 'w:cols', 'w:num'), 'colsSpace' => array(self::READ_VALUE, 'w:cols', 'w:space'), - 'topMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:top'), - 'leftMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:left'), - 'bottomMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:bottom'), - 'rightMargin' => array(self::READ_VALUE, 'w:pgMar', 'w:right'), + 'marginTop' => array(self::READ_VALUE, 'w:pgMar', 'w:top'), + 'marginLeft' => array(self::READ_VALUE, 'w:pgMar', 'w:left'), + 'marginBottom' => array(self::READ_VALUE, 'w:pgMar', 'w:bottom'), + 'marginRight' => array(self::READ_VALUE, 'w:pgMar', 'w:right'), 'headerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:header'), 'footerHeight' => array(self::READ_VALUE, 'w:pgMar', 'w:footer'), 'gutter' => array(self::READ_VALUE, 'w:pgMar', 'w:gutter'), @@ -131,7 +129,7 @@ class Document extends AbstractPart $id = $xmlReader->getAttribute('r:id', $node); $styles['hf'][$id] = array( 'method' => str_replace('w:', '', str_replace('Reference', '', $node->nodeName)), - 'type' => $xmlReader->getAttribute('w:type', $node), + 'type' => $xmlReader->getAttribute('w:type', $node), ); } } @@ -145,7 +143,6 @@ class Document extends AbstractPart * @param \PhpOffice\Common\XMLReader $xmlReader * @param \DOMElement $node * @param \PhpOffice\PhpWord\Element\Section &$section - * @return void * * @todo */ @@ -175,7 +172,6 @@ class Document extends AbstractPart * @param \PhpOffice\Common\XMLReader $xmlReader * @param \DOMElement $node * @param \PhpOffice\PhpWord\Element\Section &$section - * @return void */ private function readWSectPrNode(XMLReader $xmlReader, \DOMElement $node, Section &$section) { diff --git a/src/PhpWord/Reader/Word2007/Endnotes.php b/src/PhpWord/Reader/Word2007/Endnotes.php index 7bcafc6a..aa8b65d7 100644 --- a/src/PhpWord/Reader/Word2007/Endnotes.php +++ b/src/PhpWord/Reader/Word2007/Endnotes.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Reader/Word2007/Footnotes.php b/src/PhpWord/Reader/Word2007/Footnotes.php index 3d85af71..634f4739 100644 --- a/src/PhpWord/Reader/Word2007/Footnotes.php +++ b/src/PhpWord/Reader/Word2007/Footnotes.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -45,13 +45,9 @@ class Footnotes extends AbstractPart * Read (footnotes|endnotes).xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { - $getMethod = "get{$this->collection}"; - $collection = $phpWord->$getMethod()->getItems(); - $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); $nodes = $xmlReader->getElements('*'); @@ -61,17 +57,41 @@ class Footnotes extends AbstractPart $type = $xmlReader->getAttribute('w:type', $node); // Avoid w:type "separator" and "continuationSeparator" - // Only look for or without w:type attribute - if (is_null($type) && isset($collection[$id])) { - $element = $collection[$id]; - $pNodes = $xmlReader->getElements('w:p/*', $node); - foreach ($pNodes as $pNode) { - $this->readRun($xmlReader, $pNode, $element, $this->collection); + // Only look for or without w:type attribute, or with w:type = normal + if ((is_null($type) || $type === 'normal')) { + $element = $this->getElement($phpWord, $id); + if ($element !== null) { + $pNodes = $xmlReader->getElements('w:p/*', $node); + foreach ($pNodes as $pNode) { + $this->readRun($xmlReader, $pNode, $element, $this->collection); + } + $addMethod = "add{$this->element}"; + $phpWord->$addMethod($element); } - $addMethod = "add{$this->element}"; - $phpWord->$addMethod($element); } } } } + + /** + * Searches for the element with the given relationId + * + * @param PhpWord $phpWord + * @param int $relationId + * @return \PhpOffice\PhpWord\Element\AbstractContainer|null + */ + private function getElement(PhpWord $phpWord, $relationId) + { + $getMethod = "get{$this->collection}"; + $collection = $phpWord->$getMethod()->getItems(); + + //not found by key, looping to search by relationId + foreach ($collection as $collectionElement) { + if ($collectionElement->getRelationId() == $relationId) { + return $collectionElement; + } + } + + return null; + } } diff --git a/src/PhpWord/Reader/Word2007/Numbering.php b/src/PhpWord/Reader/Word2007/Numbering.php index a00a6307..3f57cbf8 100644 --- a/src/PhpWord/Reader/Word2007/Numbering.php +++ b/src/PhpWord/Reader/Word2007/Numbering.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -31,7 +31,6 @@ class Numbering extends AbstractPart * Read numbering.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { @@ -92,7 +91,7 @@ class Numbering extends AbstractPart * * @param \PhpOffice\Common\XMLReader $xmlReader * @param \DOMElement $subnode - * @param integer $levelId + * @param int $levelId * @return array */ private function readLevel(XMLReader $xmlReader, \DOMElement $subnode, $levelId) diff --git a/src/PhpWord/Reader/Word2007/Settings.php b/src/PhpWord/Reader/Word2007/Settings.php new file mode 100644 index 00000000..3084943b --- /dev/null +++ b/src/PhpWord/Reader/Word2007/Settings.php @@ -0,0 +1,201 @@ +getDomFromZip($this->docFile, $this->xmlFile); + + $docSettings = $phpWord->getSettings(); + + $nodes = $xmlReader->getElements('*'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $name = str_replace('w:', '', $node->nodeName); + $value = $xmlReader->getAttribute('w:val', $node); + $method = 'set' . $name; + + if (in_array($name, $this::$booleanProperties)) { + if ($value == 'false') { + $docSettings->$method(false); + } else { + $docSettings->$method(true); + } + } elseif (method_exists($this, $method)) { + $this->$method($xmlReader, $phpWord, $node); + } elseif (method_exists($docSettings, $method)) { + $docSettings->$method($value); + } + } + } + } + + /** + * Sets the document Language + * + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setThemeFontLang(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $val = $xmlReader->getAttribute('w:val', $node); + $eastAsia = $xmlReader->getAttribute('w:eastAsia', $node); + $bidi = $xmlReader->getAttribute('w:bidi', $node); + + $themeFontLang = new Language(); + $themeFontLang->setLatin($val); + $themeFontLang->setEastAsia($eastAsia); + $themeFontLang->setBidirectional($bidi); + + $phpWord->getSettings()->setThemeFontLang($themeFontLang); + } + + /** + * Sets the document protection + * + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setDocumentProtection(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $documentProtection = $phpWord->getSettings()->getDocumentProtection(); + + $edit = $xmlReader->getAttribute('w:edit', $node); + if ($edit !== null) { + $documentProtection->setEditing($edit); + } + } + + /** + * Sets the proof state + * + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setProofState(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $proofState = $phpWord->getSettings()->getProofState(); + + $spelling = $xmlReader->getAttribute('w:spelling', $node); + $grammar = $xmlReader->getAttribute('w:grammar', $node); + + if ($spelling !== null) { + $proofState->setSpelling($spelling); + } + if ($grammar !== null) { + $proofState->setGrammar($grammar); + } + } + + /** + * Sets the proof state + * + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setZoom(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $percent = $xmlReader->getAttribute('w:percent', $node); + $val = $xmlReader->getAttribute('w:val', $node); + + if ($percent !== null || $val !== null) { + $phpWord->getSettings()->setZoom($percent === null ? $val : $percent); + } + } + + /** + * Set the Revision view + * + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setRevisionView(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $revisionView = new TrackChangesView(); + $revisionView->setMarkup(filter_var($xmlReader->getAttribute('w:markup', $node), FILTER_VALIDATE_BOOLEAN)); + $revisionView->setComments($xmlReader->getAttribute('w:comments', $node)); + $revisionView->setInsDel(filter_var($xmlReader->getAttribute('w:insDel', $node), FILTER_VALIDATE_BOOLEAN)); + $revisionView->setFormatting(filter_var($xmlReader->getAttribute('w:formatting', $node), FILTER_VALIDATE_BOOLEAN)); + $revisionView->setInkAnnotations(filter_var($xmlReader->getAttribute('w:inkAnnotations', $node), FILTER_VALIDATE_BOOLEAN)); + $phpWord->getSettings()->setRevisionView($revisionView); + } + + /** + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setConsecutiveHyphenLimit(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $value = $xmlReader->getAttribute('w:val', $node); + + if ($value !== null) { + $phpWord->getSettings()->setConsecutiveHyphenLimit($value); + } + } + + /** + * @param XMLReader $xmlReader + * @param PhpWord $phpWord + * @param \DOMElement $node + */ + protected function setHyphenationZone(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node) + { + $value = $xmlReader->getAttribute('w:val', $node); + + if ($value !== null) { + $phpWord->getSettings()->setHyphenationZone($value); + } + } +} diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php index 645c9830..f343ad92 100644 --- a/src/PhpWord/Reader/Word2007/Styles.php +++ b/src/PhpWord/Reader/Word2007/Styles.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Reader\Word2007; use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Style\Language; /** * Styles reader @@ -31,13 +32,34 @@ class Styles extends AbstractPart * Read styles.xml. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ public function read(PhpWord $phpWord) { $xmlReader = new XMLReader(); $xmlReader->getDomFromZip($this->docFile, $this->xmlFile); + $fontDefaults = $xmlReader->getElement('w:docDefaults/w:rPrDefault'); + if ($fontDefaults !== null) { + $fontDefaultStyle = $this->readFontStyle($xmlReader, $fontDefaults); + if (array_key_exists('name', $fontDefaultStyle)) { + $phpWord->setDefaultFontName($fontDefaultStyle['name']); + } + if (array_key_exists('size', $fontDefaultStyle)) { + $phpWord->setDefaultFontSize($fontDefaultStyle['size']); + } + if (array_key_exists('lang', $fontDefaultStyle)) { + $phpWord->getSettings()->setThemeFontLang(new Language($fontDefaultStyle['lang'])); + } + } + + $paragraphDefaults = $xmlReader->getElement('w:docDefaults/w:pPrDefault'); + if ($paragraphDefaults !== null) { + $paragraphDefaultStyle = $this->readParagraphStyle($xmlReader, $paragraphDefaults); + if ($paragraphDefaultStyle != null) { + $phpWord->setDefaultParagraphStyle($paragraphDefaultStyle); + } + } + $nodes = $xmlReader->getElements('w:style'); if ($nodes->length > 0) { foreach ($nodes as $node) { @@ -49,7 +71,6 @@ class Styles extends AbstractPart preg_match('/Heading(\d)/', $name, $headingMatches); // $default = ($xmlReader->getAttribute('w:default', $node) == 1); switch ($type) { - case 'paragraph': $paragraphStyle = $this->readParagraphStyle($xmlReader, $node); $fontStyle = $this->readFontStyle($xmlReader, $node); @@ -65,14 +86,12 @@ class Styles extends AbstractPart } } break; - case 'character': $fontStyle = $this->readFontStyle($xmlReader, $node); if (!empty($fontStyle)) { $phpWord->addFontStyle($name, $fontStyle); } break; - case 'table': $tStyle = $this->readTableStyle($xmlReader, $node); if (!empty($tStyle)) { diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index e049c46e..8de1a8df 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -30,8 +30,8 @@ class Settings * @const string */ const ZIPARCHIVE = 'ZipArchive'; - const PCLZIP = 'PclZip'; - const OLD_LIB = 'PhpOffice\\PhpWord\\Shared\\ZipArchive'; // @deprecated 0.11 + const PCLZIP = 'PclZip'; + const OLD_LIB = 'PhpOffice\\PhpWord\\Shared\\ZipArchive'; // @deprecated 0.11 /** * PDF rendering libraries @@ -39,8 +39,8 @@ class Settings * @const string */ const PDF_RENDERER_DOMPDF = 'DomPDF'; - const PDF_RENDERER_TCPDF = 'TCPDF'; - const PDF_RENDERER_MPDF = 'MPDF'; + const PDF_RENDERER_TCPDF = 'TCPDF'; + const PDF_RENDERER_MPDF = 'MPDF'; /** * Measurement units multiplication factor @@ -53,12 +53,12 @@ class Settings * * @const string */ - const UNIT_TWIP = 'twip'; // = 1/20 point - const UNIT_CM = 'cm'; - const UNIT_MM = 'mm'; - const UNIT_INCH = 'inch'; + const UNIT_TWIP = 'twip'; // = 1/20 point + const UNIT_CM = 'cm'; + const UNIT_MM = 'mm'; + const UNIT_INCH = 'inch'; const UNIT_POINT = 'point'; // = 1/72 inch - const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points + const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points /** * Default font settings @@ -133,7 +133,7 @@ class Settings * @var bool */ private static $outputEscapingEnabled = false; - + /** * Return the compatibility option used by the XMLWriter * @@ -154,7 +154,7 @@ class Settings */ public static function setCompatibility($compatibility) { - $compatibility = (bool)$compatibility; + $compatibility = (bool) $compatibility; self::$xmlWriterCompatibility = $compatibility; return true; @@ -180,6 +180,7 @@ class Settings { if (in_array($zipClass, array(self::PCLZIP, self::ZIPARCHIVE, self::OLD_LIB))) { self::$zipClass = $zipClass; + return true; } @@ -229,7 +230,6 @@ class Settings return true; } - /** * Return the directory path to the PDF Rendering Library. * @@ -275,7 +275,7 @@ class Settings public static function setMeasurementUnit($value) { $units = array(self::UNIT_TWIP, self::UNIT_CM, self::UNIT_MM, self::UNIT_INCH, - self::UNIT_POINT, self::UNIT_PICA); + self::UNIT_POINT, self::UNIT_PICA, ); if (!in_array($value, $units)) { return false; } @@ -289,9 +289,7 @@ class Settings * * @since 0.12.0 * - * @param string $tempDir The user defined path to temporary directory. - * - * @return void + * @param string $tempDir The user defined path to temporary directory */ public static function setTempDir($tempDir) { @@ -307,10 +305,10 @@ class Settings */ public static function getTempDir() { - $tempDir = sys_get_temp_dir(); - if (!empty(self::$tempDir)) { $tempDir = self::$tempDir; + } else { + $tempDir = sys_get_temp_dir(); } return $tempDir; @@ -318,10 +316,8 @@ class Settings /** * @since 0.13.0 - * - * @return boolean * - * @codeCoverageIgnore + * @return bool */ public static function isOutputEscapingEnabled() { @@ -330,10 +326,8 @@ class Settings /** * @since 0.13.0 - * - * @param boolean $outputEscapingEnabled * - * @codeCoverageIgnore + * @param bool $outputEscapingEnabled */ public static function setOutputEscapingEnabled($outputEscapingEnabled) { @@ -360,6 +354,7 @@ class Settings { if (is_string($value) && trim($value) !== '') { self::$defaultFontName = $value; + return true; } @@ -369,7 +364,7 @@ class Settings /** * Get default font size * - * @return integer + * @return int */ public static function getDefaultFontSize() { @@ -384,9 +379,10 @@ class Settings */ public static function setDefaultFontSize($value) { - $value = intval($value); + $value = (int) $value; if ($value > 0) { self::$defaultFontSize = $value; + return true; } diff --git a/src/PhpWord/Shared/AbstractEnum.php b/src/PhpWord/Shared/AbstractEnum.php new file mode 100644 index 00000000..f2375d87 --- /dev/null +++ b/src/PhpWord/Shared/AbstractEnum.php @@ -0,0 +1,75 @@ +getConstants(); + } + + return self::$constCacheArray[$calledClass]; + } + + /** + * Returns all values for this enum + * + * @return array + */ + public static function values() + { + return array_values(self::getConstants()); + } + + /** + * Returns true the value is valid for this enum + * + * @param string $value + * @return bool true if value is valid + */ + public static function isValid($value) + { + $values = array_values(self::getConstants()); + + return in_array($value, $values, true); + } + + /** + * Validates that the value passed is a valid value + * + * @param string $value + * @throws \InvalidArgumentException if the value passed is not valid for this enum + */ + public static function validate($value) + { + if (!self::isValid($value)) { + $calledClass = get_called_class(); + $values = array_values(self::getConstants()); + throw new \InvalidArgumentException("$value is not a valid value for $calledClass, possible values are " . implode(', ', $values)); + } + } +} diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index e5cb5b25..c53f0030 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,17 +22,18 @@ namespace PhpOffice\PhpWord\Shared; */ class Converter { - const INCH_TO_CM = 2.54; - const INCH_TO_TWIP = 1440; - const INCH_TO_PIXEL = 96; - const INCH_TO_POINT = 72; - const PIXEL_TO_EMU = 9525; - const DEGREE_TO_ANGLE = 60000; + const INCH_TO_CM = 2.54; + const INCH_TO_TWIP = 1440; + const INCH_TO_PIXEL = 96; + const INCH_TO_POINT = 72; + const INCH_TO_PICA = 6; + const PIXEL_TO_EMU = 9525; + const DEGREE_TO_ANGLE = 60000; /** * Convert centimeter to twip * - * @param int $centimeter + * @param float $centimeter * @return float */ public static function cmToTwip($centimeter = 1) @@ -43,7 +44,7 @@ class Converter /** * Convert centimeter to inch * - * @param int $centimeter + * @param float $centimeter * @return float */ public static function cmToInch($centimeter = 1) @@ -54,7 +55,7 @@ class Converter /** * Convert centimeter to pixel * - * @param int $centimeter + * @param float $centimeter * @return float */ public static function cmToPixel($centimeter = 1) @@ -65,7 +66,7 @@ class Converter /** * Convert centimeter to point * - * @param int $centimeter + * @param float $centimeter * @return float */ public static function cmToPoint($centimeter = 1) @@ -76,8 +77,8 @@ class Converter /** * Convert centimeter to EMU * - * @param int $centimeter - * @return int + * @param float $centimeter + * @return float */ public static function cmToEmu($centimeter = 1) { @@ -87,8 +88,8 @@ class Converter /** * Convert inch to twip * - * @param int $inch - * @return int + * @param float $inch + * @return float */ public static function inchToTwip($inch = 1) { @@ -98,7 +99,7 @@ class Converter /** * Convert inch to centimeter * - * @param int $inch + * @param float $inch * @return float */ public static function inchToCm($inch = 1) @@ -109,8 +110,8 @@ class Converter /** * Convert inch to pixel * - * @param int $inch - * @return int + * @param float $inch + * @return float */ public static function inchToPixel($inch = 1) { @@ -120,8 +121,8 @@ class Converter /** * Convert inch to point * - * @param int $inch - * @return int + * @param float $inch + * @return float */ public static function inchToPoint($inch = 1) { @@ -131,8 +132,8 @@ class Converter /** * Convert inch to EMU * - * @param int $inch - * @return int + * @param float $inch + * @return float */ public static function inchToEmu($inch = 1) { @@ -143,7 +144,7 @@ class Converter * Convert pixel to twip * * @param int $pixel - * @return int + * @return float */ public static function pixelToTwip($pixel = 1) { @@ -187,7 +188,7 @@ class Converter * Convert point to twip unit * * @param int $point - * @return int + * @return float */ public static function pointToTwip($point = 1) { @@ -209,7 +210,7 @@ class Converter * Convert point to EMU * * @param int $point - * @return int + * @return float */ public static function pointToEmu($point = 1) { @@ -220,13 +221,24 @@ class Converter * Convert EMU to pixel * * @param int $emu - * @return int + * @return float */ public static function emuToPixel($emu = 1) { return round($emu / self::PIXEL_TO_EMU); } + /** + * Convert pica to point + * + * @param int $pica + * @return float + */ + public static function picaToPoint($pica = 1) + { + return $pica / self::INCH_TO_PICA * self::INCH_TO_POINT; + } + /** * Convert degree to angle * @@ -235,7 +247,7 @@ class Converter */ public static function degreeToAngle($degree = 1) { - return (int)round($degree * self::DEGREE_TO_ANGLE); + return (int) round($degree * self::DEGREE_TO_ANGLE); } /** @@ -275,4 +287,49 @@ class Converter return array($red, $green, $blue); } + + /** + * Transforms a size in CSS format (eg. 10px, 10px, ...) to points + * + * @param string $value + * @return float + */ + public static function cssToPoint($value) + { + if ($value == '0') { + return 0; + } + if (preg_match('/^[+-]?([0-9]+\.?[0-9]*)?(px|em|ex|%|in|cm|mm|pt|pc)$/i', $value, $matches)) { + $size = $matches[1]; + $unit = $matches[2]; + + switch ($unit) { + case 'pt': + return $size; + case 'px': + return self::pixelToPoint($size); + case 'cm': + return self::cmToPoint($size); + case 'mm': + return self::cmToPoint($size / 10); + case 'in': + return self::inchToPoint($size); + case 'pc': + return self::picaToPoint($size); + } + } + + return null; + } + + /** + * Transforms a size in CSS format (eg. 10px, 10px, ...) to twips + * + * @param string $value + * @return float + */ + public static function cssToTwip($value) + { + return self::pointToTwip(self::cssToPoint($value)); + } } diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index c551fd5b..a92d9047 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -10,14 +10,19 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Shared; use PhpOffice\PhpWord\Element\AbstractContainer; +use PhpOffice\PhpWord\Element\Row; +use PhpOffice\PhpWord\Element\Table; +use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\NumberFormat; /** * Common Html functions @@ -26,22 +31,32 @@ use PhpOffice\PhpWord\Element\AbstractContainer; */ class Html { + private static $listIndex = 0; + private static $xpath; + private static $options; + /** * Add HTML parts. * * Note: $stylesheet parameter is removed to avoid PHPMD error for unused parameter + * Warning: Do not pass user-generated HTML here, as that would allow an attacker to read arbitrary + * files or perform server-side request forgery by passing local file paths or URLs in . * * @param \PhpOffice\PhpWord\Element\AbstractContainer $element Where the parts need to be added * @param string $html The code to parse * @param bool $fullHTML If it's a full HTML, no need to add 'body' tag - * @return void + * @param bool $preserveWhiteSpace If false, the whitespaces between nodes will be removed + * @param array $options: + * + IMG_SRC_SEARCH: optional to speed up images loading from remote url when files can be found locally + * + IMG_SRC_REPLACE: optional to speed up images loading from remote url when files can be found locally */ - public static function addHtml($element, $html, $fullHTML = false) + public static function addHtml($element, $html, $fullHTML = false, $preserveWhiteSpace = true, $options = null) { /* * @todo parse $stylesheet for default styles. Should result in an array based on id, class and element, * which could be applied when such an element occurs in the parseNode function. */ + self::$options = $options; // Preprocess: remove all line ends, decode HTML entity, // fix ampersand and angle brackets and add body tag for HTML fragments @@ -56,9 +71,11 @@ class Html } // Load DOM + libxml_disable_entity_loader(true); $dom = new \DOMDocument(); - $dom->preserveWhiteSpace = true; + $dom->preserveWhiteSpace = $preserveWhiteSpace; $dom->loadHTML($html, LIBXML_NOWARNING); + self::$xpath = new \DOMXPath($dom); $node = $dom->getElementsByTagName('body'); self::parseNode($node->item(0), $element); } @@ -80,6 +97,12 @@ class Html case 'style': $styles = self::parseStyle($attribute, $styles); break; + case 'align': + $styles['alignment'] = self::mapAlign($attribute->value); + break; + case 'lang': + $styles['lang'] = $attribute->value; + break; } } } @@ -94,12 +117,11 @@ class Html * @param \PhpOffice\PhpWord\Element\AbstractContainer $element object to add an element corresponding with the node * @param array $styles Array with all styles * @param array $data Array to transport data to a next level in the DOM tree, for example level of listitems - * @return void */ protected static function parseNode($node, $element, $styles = array(), $data = array()) { // Populate styles array - $styleTypes = array('font', 'paragraph', 'list'); + $styleTypes = array('font', 'paragraph', 'list', 'table', 'row', 'cell'); foreach ($styleTypes as $styleType) { if (!isset($styles[$styleType])) { $styles[$styleType] = array(); @@ -118,15 +140,24 @@ class Html 'h6' => array('Heading', null, $element, $styles, null, 'Heading6', null), '#text' => array('Text', $node, $element, $styles, null, null, null), 'strong' => array('Property', null, null, $styles, null, 'bold', true), + 'b' => array('Property', null, null, $styles, null, 'bold', true), 'em' => array('Property', null, null, $styles, null, 'italic', true), + 'i' => array('Property', null, null, $styles, null, 'italic', true), + 'u' => array('Property', null, null, $styles, null, 'underline', 'single'), 'sup' => array('Property', null, null, $styles, null, 'superScript', true), 'sub' => array('Property', null, null, $styles, null, 'subScript', true), - 'table' => array('Table', $node, $element, $styles, null, 'addTable', true), - 'tr' => array('Table', $node, $element, $styles, null, 'addRow', true), - 'td' => array('Table', $node, $element, $styles, null, 'addCell', true), - 'ul' => array('List', null, null, $styles, $data, 3, null), - 'ol' => array('List', null, null, $styles, $data, 7, null), + 'span' => array('Span', $node, null, $styles, null, null, null), + 'font' => array('Span', $node, null, $styles, null, null, null), + 'table' => array('Table', $node, $element, $styles, null, null, null), + 'tr' => array('Row', $node, $element, $styles, null, null, null), + 'td' => array('Cell', $node, $element, $styles, null, null, null), + 'th' => array('Cell', $node, $element, $styles, null, null, null), + 'ul' => array('List', $node, $element, $styles, $data, null, null), + 'ol' => array('List', $node, $element, $styles, $data, null, null), 'li' => array('ListItem', $node, $element, $styles, $data, null, null), + 'img' => array('Image', $node, $element, $styles, null, null, null), + 'br' => array('LineBreak', null, $element, $styles, null, null, null), + 'a' => array('Link', $node, $element, $styles, null, null, null), ); $newElement = null; @@ -168,15 +199,14 @@ class Html * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array $styles * @param array $data - * @return void */ private static function parseChildNodes($node, $element, $styles, $data) { if ('li' != $node->nodeName) { $cNodes = $node->childNodes; - if (count($cNodes) > 0) { + if (!empty($cNodes)) { foreach ($cNodes as $cNode) { - if ($element instanceof AbstractContainer) { + if ($element instanceof AbstractContainer || $element instanceof Table || $element instanceof Row) { self::parseNode($cNode, $element, $styles, $data); } } @@ -194,7 +224,7 @@ class Html */ private static function parseParagraph($node, $element, &$styles) { - $styles['paragraph'] = self::parseInlineStyle($node, $styles['paragraph']); + $styles['paragraph'] = self::recursiveParseStylesInHierarchy($node, $styles['paragraph']); $newElement = $element->addTextRun($styles['paragraph']); return $newElement; @@ -225,19 +255,19 @@ class Html * @param \DOMNode $node * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles - * @return null */ private static function parseText($node, $element, &$styles) { - $styles['font'] = self::parseInlineStyle($node, $styles['font']); + $styles['font'] = self::recursiveParseStylesInHierarchy($node, $styles['font']); - // Commented as source of bug #257. `method_exists` doesn't seems to work properly in this case. - // @todo Find better error checking for this one - // if (method_exists($element, 'addText')) { + //alignment applies on paragraph, not on font. Let's copy it there + if (isset($styles['font']['alignment']) && is_array($styles['paragraph'])) { + $styles['paragraph']['alignment'] = $styles['font']['alignment']; + } + + if (is_callable(array($element, 'addText'))) { $element->addText($node->nodeValue, $styles['font'], $styles['paragraph']); - // } - - return null; + } } /** @@ -246,13 +276,21 @@ class Html * @param array &$styles * @param string $argument1 Style name * @param string $argument2 Style value - * @return null */ private static function parseProperty(&$styles, $argument1, $argument2) { $styles['font'][$argument1] = $argument2; + } - return null; + /** + * Parse span node + * + * @param \DOMNode $node + * @param array &$styles + */ + private static function parseSpan($node, &$styles) + { + self::parseInlineStyle($node, $styles['font']); } /** @@ -261,50 +299,168 @@ class Html * @param \DOMNode $node * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles - * @param string $argument1 Method name - * @return \PhpOffice\PhpWord\Element\AbstractContainer $element + * @return Table $element * * @todo As soon as TableItem, RowItem and CellItem support relative width and height */ - private static function parseTable($node, $element, &$styles, $argument1) + private static function parseTable($node, $element, &$styles) { - $styles['paragraph'] = self::parseInlineStyle($node, $styles['paragraph']); + $elementStyles = self::parseInlineStyle($node, $styles['table']); - $newElement = $element->$argument1(); + $newElement = $element->addTable($elementStyles); // $attributes = $node->attributes; // if ($attributes->getNamedItem('width') !== null) { - // $newElement->setWidth($attributes->getNamedItem('width')->value); + // $newElement->setWidth($attributes->getNamedItem('width')->value); // } // if ($attributes->getNamedItem('height') !== null) { - // $newElement->setHeight($attributes->getNamedItem('height')->value); + // $newElement->setHeight($attributes->getNamedItem('height')->value); // } // if ($attributes->getNamedItem('width') !== null) { - // $newElement=$element->addCell($width=$attributes->getNamedItem('width')->value); + // $newElement=$element->addCell($width=$attributes->getNamedItem('width')->value); // } return $newElement; } + /** + * Parse a table row + * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\Table $element + * @param array &$styles + * @return Row $element + */ + private static function parseRow($node, $element, &$styles) + { + $rowStyles = self::parseInlineStyle($node, $styles['row']); + if ($node->parentNode->nodeName == 'thead') { + $rowStyles['tblHeader'] = true; + } + + return $element->addRow(null, $rowStyles); + } + + /** + * Parse table cell + * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\Table $element + * @param array &$styles + * @return \PhpOffice\PhpWord\Element\Cell|\PhpOffice\PhpWord\Element\TextRun $element + */ + private static function parseCell($node, $element, &$styles) + { + $cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']); + + $colspan = $node->getAttribute('colspan'); + if (!empty($colspan)) { + $cellStyles['gridSpan'] = $colspan - 0; + } + $cell = $element->addCell(null, $cellStyles); + + if (self::shouldAddTextRun($node)) { + return $cell->addTextRun(self::parseInlineStyle($node, $styles['paragraph'])); + } + + return $cell; + } + + /** + * Checks if $node contains an HTML element that cannot be added to TextRun + * + * @param \DOMNode $node + * @return bool Returns true if the node contains an HTML element that cannot be added to TextRun + */ + private static function shouldAddTextRun(\DOMNode $node) + { + $containsBlockElement = self::$xpath->query('.//table|./p|./ul|./ol', $node)->length > 0; + if ($containsBlockElement) { + return false; + } + + return true; + } + + /** + * Recursively parses styles on parent nodes + * TODO if too slow, add caching of parent nodes, !! everything is static here so watch out for concurrency !! + * + * @param \DOMNode $node + * @param array &$styles + */ + private static function recursiveParseStylesInHierarchy(\DOMNode $node, array $style) + { + $parentStyle = self::parseInlineStyle($node, array()); + $style = array_merge($parentStyle, $style); + if ($node->parentNode != null && XML_ELEMENT_NODE == $node->parentNode->nodeType) { + $style = self::recursiveParseStylesInHierarchy($node->parentNode, $style); + } + + return $style; + } + /** * Parse list node * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * @param array &$data - * @param string $argument1 List type - * @return null */ - private static function parseList(&$styles, &$data, $argument1) + private static function parseList($node, $element, &$styles, &$data) { + $isOrderedList = $node->nodeName === 'ol'; if (isset($data['listdepth'])) { $data['listdepth']++; } else { $data['listdepth'] = 0; + $styles['list'] = 'listStyle_' . self::$listIndex++; + $element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList)); } - $styles['list']['listType'] = $argument1; + if ($node->parentNode->nodeName === 'li') { + return $element->getParent(); + } + } - return null; + /** + * @param bool $isOrderedList + * @return array + */ + private static function getListStyle($isOrderedList) + { + if ($isOrderedList) { + return array( + 'type' => 'multilevel', + 'levels' => array( + array('format' => NumberFormat::DECIMAL, 'text' => '%1.', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360), + array('format' => NumberFormat::LOWER_LETTER, 'text' => '%2.', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360), + array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%3.', 'alignment' => 'right', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 180), + array('format' => NumberFormat::DECIMAL, 'text' => '%4.', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360), + array('format' => NumberFormat::LOWER_LETTER, 'text' => '%5.', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360), + array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%6.', 'alignment' => 'right', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 180), + array('format' => NumberFormat::DECIMAL, 'text' => '%7.', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360), + array('format' => NumberFormat::LOWER_LETTER, 'text' => '%8.', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360), + array('format' => NumberFormat::LOWER_ROMAN, 'text' => '%9.', 'alignment' => 'right', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 180), + ), + ); + } + + return array( + 'type' => 'hybridMultilevel', + 'levels' => array( + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'), + array('format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'), + ), + ); } /** @@ -314,7 +470,6 @@ class Html * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * @param array $data - * @return null * * @todo This function is almost the same like `parseChildNodes`. Merged? * @todo As soon as ListItem inherits from AbstractContainer or TextRun delete parsing part of childNodes @@ -322,17 +477,12 @@ class Html private static function parseListItem($node, $element, &$styles, $data) { $cNodes = $node->childNodes; - if (count($cNodes) > 0) { - $text = ''; + if (!empty($cNodes)) { + $listRun = $element->addListItemRun($data['listdepth'], $styles['list'], $styles['paragraph']); foreach ($cNodes as $cNode) { - if ($cNode->nodeName == '#text') { - $text = $cNode->nodeValue; - } + self::parseNode($cNode, $listRun, $styles, $data); } - $element->addListItem($text, $data['listdepth'], $styles['font'], $styles['list'], $styles['paragraph']); } - - return null; } /** @@ -345,8 +495,9 @@ class Html private static function parseStyle($attribute, $styles) { $properties = explode(';', trim($attribute->value, " \t\n\r\0\x0B;")); + foreach ($properties as $property) { - list($cKey, $cValue) = explode(':', $property, 2); + list($cKey, $cValue) = array_pad(explode(':', $property, 2), 2, null); $cValue = trim($cValue); switch (trim($cKey)) { case 'text-decoration': @@ -360,17 +511,273 @@ class Html } break; case 'text-align': - $styles['alignment'] = $cValue; // todo: any mapping? + $styles['alignment'] = self::mapAlign($cValue); + break; + case 'direction': + $styles['rtl'] = $cValue === 'rtl'; + break; + case 'font-size': + $styles['size'] = Converter::cssToPoint($cValue); + break; + case 'font-family': + $cValue = array_map('trim', explode(',', $cValue)); + $styles['name'] = ucwords($cValue[0]); break; case 'color': - $styles['color'] = trim($cValue, "#"); + $styles['color'] = trim($cValue, '#'); break; case 'background-color': - $styles['bgColor'] = trim($cValue, "#"); + $styles['bgColor'] = trim($cValue, '#'); + break; + case 'line-height': + $matches = array(); + if (preg_match('/([0-9]+\.?[0-9]*[a-z]+)/', $cValue, $matches)) { + $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT; + $spacing = Converter::cssToTwip($matches[1]) / \PhpOffice\PhpWord\Style\Paragraph::LINE_HEIGHT; + } elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) { + $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; + $spacing = ((int) $matches[1]) / 100; + } else { + $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; + $spacing = $cValue; + } + $styles['spacingLineRule'] = $spacingLineRule; + $styles['lineHeight'] = $spacing; + break; + case 'text-indent': + $styles['indentation']['firstLine'] = Converter::cssToTwip($cValue); + break; + case 'font-weight': + $tValue = false; + if (preg_match('#bold#', $cValue)) { + $tValue = true; // also match bolder + } + $styles['bold'] = $tValue; + break; + case 'font-style': + $tValue = false; + if (preg_match('#(?:italic|oblique)#', $cValue)) { + $tValue = true; + } + $styles['italic'] = $tValue; + break; + case 'margin-top': + $styles['spaceBefore'] = Converter::cssToPoint($cValue); + break; + case 'margin-bottom': + $styles['spaceAfter'] = Converter::cssToPoint($cValue); + break; + case 'border-color': + $styles['color'] = trim($cValue, '#'); + break; + case 'border-width': + $styles['borderSize'] = Converter::cssToPoint($cValue); + break; + case 'border-style': + $styles['borderStyle'] = self::mapBorderStyle($cValue); + break; + case 'width': + if (preg_match('/([0-9]+[a-z]+)/', $cValue, $matches)) { + $styles['width'] = Converter::cssToTwip($matches[1]); + $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP; + } elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) { + $styles['width'] = $matches[1] * 50; + $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT; + } elseif (preg_match('/([0-9]+)/', $cValue, $matches)) { + $styles['width'] = $matches[1]; + $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::AUTO; + } + break; + case 'border': + if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/', $cValue, $matches)) { + $styles['borderSize'] = Converter::cssToPoint($matches[1]); + $styles['borderColor'] = trim($matches[2], '#'); + $styles['borderStyle'] = self::mapBorderStyle($matches[3]); + } break; } } return $styles; } + + /** + * Parse image node + * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element + * + * @return \PhpOffice\PhpWord\Element\Image + **/ + private static function parseImage($node, $element) + { + $style = array(); + $src = null; + foreach ($node->attributes as $attribute) { + switch ($attribute->name) { + case 'src': + $src = $attribute->value; + break; + case 'width': + $width = $attribute->value; + $style['width'] = $width; + $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX; + break; + case 'height': + $height = $attribute->value; + $style['height'] = $height; + $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX; + break; + case 'style': + $styleattr = explode(';', $attribute->value); + foreach ($styleattr as $attr) { + if (strpos($attr, ':')) { + list($k, $v) = explode(':', $attr); + switch ($k) { + case 'float': + if (trim($v) == 'right') { + $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_RIGHT; + $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE; + $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE; + $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT; + $style['overlap'] = true; + } + if (trim($v) == 'left') { + $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_LEFT; + $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE; + $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE; + $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT; + $style['overlap'] = true; + } + break; + } + } + } + break; + } + } + $originSrc = $src; + if (strpos($src, 'data:image') !== false) { + $tmpDir = Settings::getTempDir() . '/'; + + $match = array(); + preg_match('/data:image\/(\w+);base64,(.+)/', $src, $match); + + $src = $imgFile = $tmpDir . uniqid() . '.' . $match[1]; + + $ifp = fopen($imgFile, 'wb'); + + if ($ifp !== false) { + fwrite($ifp, base64_decode($match[2])); + fclose($ifp); + } + } + $src = urldecode($src); + + if (!is_file($src) + && !is_null(self::$options) + && isset(self::$options['IMG_SRC_SEARCH']) + && isset(self::$options['IMG_SRC_REPLACE'])) { + $src = str_replace(self::$options['IMG_SRC_SEARCH'], self::$options['IMG_SRC_REPLACE'], $src); + } + + if (!is_file($src)) { + if ($imgBlob = @file_get_contents($src)) { + $tmpDir = Settings::getTempDir() . '/'; + $match = array(); + preg_match('/.+\.(\w+)$/', $src, $match); + $src = $tmpDir . uniqid() . '.' . $match[1]; + + $ifp = fopen($src, 'wb'); + + if ($ifp !== false) { + fwrite($ifp, $imgBlob); + fclose($ifp); + } + } + } + + if (is_file($src)) { + $newElement = $element->addImage($src, $style); + } else { + throw new \Exception("Could not load image $originSrc"); + } + + return $newElement; + } + + /** + * Transforms a CSS border style into a word border style + * + * @param string $cssBorderStyle + * @return null|string + */ + private static function mapBorderStyle($cssBorderStyle) + { + switch ($cssBorderStyle) { + case 'none': + case 'dashed': + case 'dotted': + case 'double': + return $cssBorderStyle; + default: + return 'single'; + } + } + + /** + * Transforms a HTML/CSS alignment into a \PhpOffice\PhpWord\SimpleType\Jc + * + * @param string $cssAlignment + * @return string|null + */ + private static function mapAlign($cssAlignment) + { + switch ($cssAlignment) { + case 'right': + return Jc::END; + case 'center': + return Jc::CENTER; + case 'justify': + return Jc::BOTH; + default: + return Jc::START; + } + } + + /** + * Parse line break + * + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element + */ + private static function parseLineBreak($element) + { + $element->addTextBreak(); + } + + /** + * Parse link node + * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element + * @param array $styles + */ + private static function parseLink($node, $element, &$styles) + { + $target = null; + foreach ($node->attributes as $attribute) { + switch ($attribute->name) { + case 'href': + $target = $attribute->value; + break; + } + } + $styles['font'] = self::parseInlineStyle($node, $styles['font']); + + if (strpos($target, '#') === 0) { + return $element->addLink(substr($target, 1), $node->textContent, $styles['font'], $styles['paragraph'], true); + } + + return $element->addLink($target, $node->textContent, $styles['font'], $styles['paragraph']); + } } diff --git a/src/PhpWord/Shared/OLERead.php b/src/PhpWord/Shared/OLERead.php index cf9b15d3..2e6a899e 100644 --- a/src/PhpWord/Shared/OLERead.php +++ b/src/PhpWord/Shared/OLERead.php @@ -10,11 +10,10 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ - namespace PhpOffice\PhpWord\Shared; use PhpOffice\PhpWord\Exception\Exception; @@ -111,15 +110,18 @@ class OLERead $bbdBlocks = $this->numBigBlockDepotBlocks; + // @codeCoverageIgnoreStart if ($this->numExtensionBlocks != 0) { $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4; } + // @codeCoverageIgnoreEnd for ($i = 0; $i < $bbdBlocks; ++$i) { $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos); $pos += 4; } + // @codeCoverageIgnoreStart 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); @@ -134,6 +136,7 @@ class OLERead $this->extensionBlock = self::getInt4d($this->data, $pos); } } + // @codeCoverageIgnoreEnd $pos = 0; $this->bigBlockChain = ''; @@ -189,27 +192,27 @@ class OLERead $block = self::getInt4d($this->smallBlockChain, $block*4); } - return $streamData; - } else { - $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; } + + $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE; + if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) { + ++$numBlocks; + } + + if ($numBlocks == 0) { + return '';// @codeCoverageIgnore + } + + $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; } /** @@ -294,7 +297,6 @@ class OLERead $offset += self::PROPERTY_STORAGE_BLOCK_SIZE; } - } /** diff --git a/src/PhpWord/Shared/PCLZip/pclzip.lib.php b/src/PhpWord/Shared/PCLZip/pclzip.lib.php index 4e2a496f..3fbc9327 100644 --- a/src/PhpWord/Shared/PCLZip/pclzip.lib.php +++ b/src/PhpWord/Shared/PCLZip/pclzip.lib.php @@ -25,4926 +25,2241 @@ // $Id: pclzip.lib.php,v 1.60 2009/09/30 21:01:04 vblavet Exp $ // -------------------------------------------------------------------------------- - // ----- Constants - if (!defined('PCLZIP_READ_BLOCK_SIZE')) { - define( 'PCLZIP_READ_BLOCK_SIZE', 2048 ); - } +// ----- Constants +if (!defined('PCLZIP_READ_BLOCK_SIZE')) { + define('PCLZIP_READ_BLOCK_SIZE', 2048); +} - // ----- File list separator - // In version 1.x of PclZip, the separator for file list is a space - // (which is not a very smart choice, specifically for windows paths !). - // A better separator should be a comma (,). This constant gives you the - // abilty to change that. - // However notice that changing this value, may have impact on existing - // scripts, using space separated filenames. - // Recommanded values for compatibility with older versions : - //define( 'PCLZIP_SEPARATOR', ' ' ); - // Recommanded values for smart separation of filenames. - if (!defined('PCLZIP_SEPARATOR')) { - define( 'PCLZIP_SEPARATOR', ',' ); - } +// ----- File list separator +// In version 1.x of PclZip, the separator for file list is a space +// (which is not a very smart choice, specifically for windows paths !). +// A better separator should be a comma (,). This constant gives you the +// abilty to change that. +// However notice that changing this value, may have impact on existing +// scripts, using space separated filenames. +// Recommanded values for compatibility with older versions : +//define( 'PCLZIP_SEPARATOR', ' ' ); +// Recommanded values for smart separation of filenames. +if (!defined('PCLZIP_SEPARATOR')) { + define('PCLZIP_SEPARATOR', ','); +} - // ----- Error configuration - // 0 : PclZip Class integrated error handling - // 1 : PclError external library error handling. By enabling this - // you must ensure that you have included PclError library. - // [2,...] : reserved for futur use - if (!defined('PCLZIP_ERROR_EXTERNAL')) { - define( 'PCLZIP_ERROR_EXTERNAL', 0 ); - } +// ----- Error configuration +// 0 : PclZip Class integrated error handling +// 1 : PclError external library error handling. By enabling this +// you must ensure that you have included PclError library. +// [2,...] : reserved for futur use +if (!defined('PCLZIP_ERROR_EXTERNAL')) { + define('PCLZIP_ERROR_EXTERNAL', 0); +} - // ----- Optional static temporary directory - // By default temporary files are generated in the script current - // path. - // If defined : - // - MUST BE terminated by a '/'. - // - MUST be a valid, already created directory - // Samples : - // define( 'PCLZIP_TEMPORARY_DIR', '/temp/' ); - // define( 'PCLZIP_TEMPORARY_DIR', 'C:/Temp/' ); - if (!defined('PCLZIP_TEMPORARY_DIR')) { - define( 'PCLZIP_TEMPORARY_DIR', '' ); - } +// ----- Optional static temporary directory +// By default temporary files are generated in the script current +// path. +// If defined : +// - MUST BE terminated by a '/'. +// - MUST be a valid, already created directory +// Samples : +// define( 'PCLZIP_TEMPORARY_DIR', '/temp/' ); +// define( 'PCLZIP_TEMPORARY_DIR', 'C:/Temp/' ); +if (!defined('PCLZIP_TEMPORARY_DIR')) { + define('PCLZIP_TEMPORARY_DIR', ''); +} - // ----- Optional threshold ratio for use of temporary files - // Pclzip sense the size of the file to add/extract and decide to - // use or not temporary file. The algorythm is looking for - // memory_limit of PHP and apply a ratio. - // threshold = memory_limit * ratio. - // Recommended values are under 0.5. Default 0.47. - // Samples : - // define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.5 ); - if (!defined('PCLZIP_TEMPORARY_FILE_RATIO')) { - define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.47 ); - } +// ----- Optional threshold ratio for use of temporary files +// Pclzip sense the size of the file to add/extract and decide to +// use or not temporary file. The algorythm is looking for +// memory_limit of PHP and apply a ratio. +// threshold = memory_limit * ratio. +// Recommended values are under 0.5. Default 0.47. +// Samples : +// define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.5 ); +if (!defined('PCLZIP_TEMPORARY_FILE_RATIO')) { + define('PCLZIP_TEMPORARY_FILE_RATIO', 0.47); +} // -------------------------------------------------------------------------------- // ***** UNDER THIS LINE NOTHING NEEDS TO BE MODIFIED ***** // -------------------------------------------------------------------------------- - // ----- Global variables - $g_pclzip_version = "2.8.2"; +// ----- Global variables +$g_pclzip_version = "2.8.2"; - // ----- Error codes - // -1 : Unable to open file in binary write mode - // -2 : Unable to open file in binary read mode - // -3 : Invalid parameters - // -4 : File does not exist - // -5 : Filename is too long (max. 255) - // -6 : Not a valid zip file - // -7 : Invalid extracted file size - // -8 : Unable to create directory - // -9 : Invalid archive extension - // -10 : Invalid archive format - // -11 : Unable to delete file (unlink) - // -12 : Unable to rename file (rename) - // -13 : Invalid header checksum - // -14 : Invalid archive size - define( 'PCLZIP_ERR_USER_ABORTED', 2 ); - define( 'PCLZIP_ERR_NO_ERROR', 0 ); - define( 'PCLZIP_ERR_WRITE_OPEN_FAIL', -1 ); - define( 'PCLZIP_ERR_READ_OPEN_FAIL', -2 ); - define( 'PCLZIP_ERR_INVALID_PARAMETER', -3 ); - define( 'PCLZIP_ERR_MISSING_FILE', -4 ); - define( 'PCLZIP_ERR_FILENAME_TOO_LONG', -5 ); - define( 'PCLZIP_ERR_INVALID_ZIP', -6 ); - define( 'PCLZIP_ERR_BAD_EXTRACTED_FILE', -7 ); - define( 'PCLZIP_ERR_DIR_CREATE_FAIL', -8 ); - define( 'PCLZIP_ERR_BAD_EXTENSION', -9 ); - define( 'PCLZIP_ERR_BAD_FORMAT', -10 ); - define( 'PCLZIP_ERR_DELETE_FILE_FAIL', -11 ); - define( 'PCLZIP_ERR_RENAME_FILE_FAIL', -12 ); - define( 'PCLZIP_ERR_BAD_CHECKSUM', -13 ); - define( 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14 ); - define( 'PCLZIP_ERR_MISSING_OPTION_VALUE', -15 ); - define( 'PCLZIP_ERR_INVALID_OPTION_VALUE', -16 ); - define( 'PCLZIP_ERR_ALREADY_A_DIRECTORY', -17 ); - define( 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18 ); - define( 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19 ); - define( 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20 ); - define( 'PCLZIP_ERR_DIRECTORY_RESTRICTION', -21 ); +// ----- Error codes +// -1 : Unable to open file in binary write mode +// -2 : Unable to open file in binary read mode +// -3 : Invalid parameters +// -4 : File does not exist +// -5 : Filename is too long (max. 255) +// -6 : Not a valid zip file +// -7 : Invalid extracted file size +// -8 : Unable to create directory +// -9 : Invalid archive extension +// -10 : Invalid archive format +// -11 : Unable to delete file (unlink) +// -12 : Unable to rename file (rename) +// -13 : Invalid header checksum +// -14 : Invalid archive size +define('PCLZIP_ERR_USER_ABORTED', 2); +define('PCLZIP_ERR_NO_ERROR', 0); +define('PCLZIP_ERR_WRITE_OPEN_FAIL', -1); +define('PCLZIP_ERR_READ_OPEN_FAIL', -2); +define('PCLZIP_ERR_INVALID_PARAMETER', -3); +define('PCLZIP_ERR_MISSING_FILE', -4); +define('PCLZIP_ERR_FILENAME_TOO_LONG', -5); +define('PCLZIP_ERR_INVALID_ZIP', -6); +define('PCLZIP_ERR_BAD_EXTRACTED_FILE', -7); +define('PCLZIP_ERR_DIR_CREATE_FAIL', -8); +define('PCLZIP_ERR_BAD_EXTENSION', -9); +define('PCLZIP_ERR_BAD_FORMAT', -10); +define('PCLZIP_ERR_DELETE_FILE_FAIL', -11); +define('PCLZIP_ERR_RENAME_FILE_FAIL', -12); +define('PCLZIP_ERR_BAD_CHECKSUM', -13); +define('PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14); +define('PCLZIP_ERR_MISSING_OPTION_VALUE', -15); +define('PCLZIP_ERR_INVALID_OPTION_VALUE', -16); +define('PCLZIP_ERR_ALREADY_A_DIRECTORY', -17); +define('PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18); +define('PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19); +define('PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20); +define('PCLZIP_ERR_DIRECTORY_RESTRICTION', -21); - // ----- Options values - define( 'PCLZIP_OPT_PATH', 77001 ); - define( 'PCLZIP_OPT_ADD_PATH', 77002 ); - define( 'PCLZIP_OPT_REMOVE_PATH', 77003 ); - define( 'PCLZIP_OPT_REMOVE_ALL_PATH', 77004 ); - define( 'PCLZIP_OPT_SET_CHMOD', 77005 ); - define( 'PCLZIP_OPT_EXTRACT_AS_STRING', 77006 ); - define( 'PCLZIP_OPT_NO_COMPRESSION', 77007 ); - define( 'PCLZIP_OPT_BY_NAME', 77008 ); - define( 'PCLZIP_OPT_BY_INDEX', 77009 ); - define( 'PCLZIP_OPT_BY_EREG', 77010 ); - define( 'PCLZIP_OPT_BY_PREG', 77011 ); - define( 'PCLZIP_OPT_COMMENT', 77012 ); - define( 'PCLZIP_OPT_ADD_COMMENT', 77013 ); - define( 'PCLZIP_OPT_PREPEND_COMMENT', 77014 ); - define( 'PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015 ); - define( 'PCLZIP_OPT_REPLACE_NEWER', 77016 ); - define( 'PCLZIP_OPT_STOP_ON_ERROR', 77017 ); - // Having big trouble with crypt. Need to multiply 2 long int - // which is not correctly supported by PHP ... - //define( 'PCLZIP_OPT_CRYPT', 77018 ); - define( 'PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019 ); - define( 'PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020 ); - define( 'PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020 ); // alias - define( 'PCLZIP_OPT_TEMP_FILE_ON', 77021 ); - define( 'PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021 ); // alias - define( 'PCLZIP_OPT_TEMP_FILE_OFF', 77022 ); - define( 'PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022 ); // alias +// ----- Options values +define('PCLZIP_OPT_PATH', 77001); +define('PCLZIP_OPT_ADD_PATH', 77002); +define('PCLZIP_OPT_REMOVE_PATH', 77003); +define('PCLZIP_OPT_REMOVE_ALL_PATH', 77004); +define('PCLZIP_OPT_SET_CHMOD', 77005); +define('PCLZIP_OPT_EXTRACT_AS_STRING', 77006); +define('PCLZIP_OPT_NO_COMPRESSION', 77007); +define('PCLZIP_OPT_BY_NAME', 77008); +define('PCLZIP_OPT_BY_INDEX', 77009); +define('PCLZIP_OPT_BY_EREG', 77010); +define('PCLZIP_OPT_BY_PREG', 77011); +define('PCLZIP_OPT_COMMENT', 77012); +define('PCLZIP_OPT_ADD_COMMENT', 77013); +define('PCLZIP_OPT_PREPEND_COMMENT', 77014); +define('PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015); +define('PCLZIP_OPT_REPLACE_NEWER', 77016); +define('PCLZIP_OPT_STOP_ON_ERROR', 77017); +// Having big trouble with crypt. Need to multiply 2 long int +// which is not correctly supported by PHP ... +//define( 'PCLZIP_OPT_CRYPT', 77018 ); +define('PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019); +define('PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020); +define('PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020); // alias +define('PCLZIP_OPT_TEMP_FILE_ON', 77021); +define('PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021); // alias +define('PCLZIP_OPT_TEMP_FILE_OFF', 77022); +define('PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022); // alias - // ----- File description attributes - define( 'PCLZIP_ATT_FILE_NAME', 79001 ); - define( 'PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002 ); - define( 'PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003 ); - define( 'PCLZIP_ATT_FILE_MTIME', 79004 ); - define( 'PCLZIP_ATT_FILE_CONTENT', 79005 ); - define( 'PCLZIP_ATT_FILE_COMMENT', 79006 ); +// ----- File description attributes +define('PCLZIP_ATT_FILE_NAME', 79001); +define('PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002); +define('PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003); +define('PCLZIP_ATT_FILE_MTIME', 79004); +define('PCLZIP_ATT_FILE_CONTENT', 79005); +define('PCLZIP_ATT_FILE_COMMENT', 79006); - // ----- Call backs values - define( 'PCLZIP_CB_PRE_EXTRACT', 78001 ); - define( 'PCLZIP_CB_POST_EXTRACT', 78002 ); - define( 'PCLZIP_CB_PRE_ADD', 78003 ); - define( 'PCLZIP_CB_POST_ADD', 78004 ); - /* For futur use - define( 'PCLZIP_CB_PRE_LIST', 78005 ); - define( 'PCLZIP_CB_POST_LIST', 78006 ); - define( 'PCLZIP_CB_PRE_DELETE', 78007 ); - define( 'PCLZIP_CB_POST_DELETE', 78008 ); - */ +// ----- Call backs values +define('PCLZIP_CB_PRE_EXTRACT', 78001); +define('PCLZIP_CB_POST_EXTRACT', 78002); +define('PCLZIP_CB_PRE_ADD', 78003); +define('PCLZIP_CB_POST_ADD', 78004); +/* For futur use +define( 'PCLZIP_CB_PRE_LIST', 78005 ); +define( 'PCLZIP_CB_POST_LIST', 78006 ); +define( 'PCLZIP_CB_PRE_DELETE', 78007 ); +define( 'PCLZIP_CB_POST_DELETE', 78008 ); +*/ - // -------------------------------------------------------------------------------- - // Class : PclZip - // Description : - // PclZip is the class that represent a Zip archive. - // The public methods allow the manipulation of the archive. - // Attributes : - // Attributes must not be accessed directly. - // Methods : - // PclZip() : Object creator - // create() : Creates the Zip archive - // listContent() : List the content of the Zip archive - // extract() : Extract the content of the archive - // properties() : List the properties of the archive - // -------------------------------------------------------------------------------- - class PclZip - { +// -------------------------------------------------------------------------------- +// Class : PclZip +// Description : +// PclZip is the class that represent a Zip archive. +// The public methods allow the manipulation of the archive. +// Attributes : +// Attributes must not be accessed directly. +// Methods : +// PclZip() : Object creator +// create() : Creates the Zip archive +// listContent() : List the content of the Zip archive +// extract() : Extract the content of the archive +// properties() : List the properties of the archive +// -------------------------------------------------------------------------------- +class PclZip +{ // ----- Filename of the zip file - var $zipname = ''; + public $zipname = ''; // ----- File descriptor of the zip file - var $zip_fd = 0; + public $zip_fd = 0; // ----- Internal error handling - var $error_code = 1; - var $error_string = ''; + public $error_code = 1; + public $error_string = ''; // ----- Current status of the magic_quotes_runtime // This value store the php configuration for magic_quotes // The class can then disable the magic_quotes and reset it after - var $magic_quotes_status; + public $magic_quotes_status; - // -------------------------------------------------------------------------------- - // Function : PclZip() - // Description : - // Creates a PclZip object and set the name of the associated Zip archive - // filename. - // Note that no real action is taken, if the archive does not exist it is not - // created. Use create() for that. - // -------------------------------------------------------------------------------- - function PclZip($p_zipname) - { - - // ----- Tests the zlib - if (!function_exists('gzopen')) + // -------------------------------------------------------------------------------- + // Function : PclZip() + // Description : + // Creates a PclZip object and set the name of the associated Zip archive + // filename. + // Note that no real action is taken, if the archive does not exist it is not + // created. Use create() for that. + // -------------------------------------------------------------------------------- + public function __construct($p_zipname) { - die('Abort '.basename(__FILE__).' : Missing zlib extensions'); - } - // ----- Set the attributes - $this->zipname = $p_zipname; - $this->zip_fd = 0; - $this->magic_quotes_status = -1; - - // ----- Return - return; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // create($p_filelist, $p_add_dir="", $p_remove_dir="") - // create($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two different synopsis. The first one is historical. - // This method creates a Zip Archive. The Zip file is created in the - // filesystem. The files and directories indicated in $p_filelist - // are added in the archive. See the parameters description for the - // supported format of $p_filelist. - // When a directory is in the list, the directory and its content is added - // in the archive. - // In this synopsis, the function takes an optional variable list of - // options. See bellow the supported options. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function create($p_filelist) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = array(); - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove from the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - //, PCLZIP_OPT_CRYPT => 'optional' - )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } - else if ($v_size > 2) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Invalid number / type of arguments"); - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = array(); - $v_att_list = array(); - $v_filedescr_list = array(); - $p_result_list = array(); - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } - - // ----- The list is a list of string names - else { - $v_string_list = $p_filelist; - } - } - - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) { - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } - - // ----- Invalid variable type for $p_filelist - else { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); - return 0; - } - - // ----- Reformat the string list - if (sizeof($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - if ($v_string != '') { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - else { - } - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes - = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' - ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' - ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' - ,PCLZIP_ATT_FILE_MTIME => 'optional' - ,PCLZIP_ATT_FILE_CONTENT => 'optional' - ,PCLZIP_ATT_FILE_COMMENT => 'optional' - ); - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, - $v_filedescr_list[], - $v_options, - $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // add($p_filelist, $p_add_dir="", $p_remove_dir="") - // add($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two synopsis. The first one is historical. - // This methods add the list of files in an existing archive. - // If a file with the same name already exists, it is added at the end of the - // archive, the first one is still present. - // If the archive does not exist, it is created. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_COMMENT : - // PCLZIP_OPT_ADD_COMMENT : - // PCLZIP_OPT_PREPEND_COMMENT : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function add($p_filelist) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Set default values - $v_options = array(); - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional', - PCLZIP_OPT_COMMENT => 'optional', - PCLZIP_OPT_ADD_COMMENT => 'optional', - PCLZIP_OPT_PREPEND_COMMENT => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - //, PCLZIP_OPT_CRYPT => 'optional' - )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Init - $v_string_list = array(); - $v_att_list = array(); - $v_filedescr_list = array(); - $p_result_list = array(); - - // ----- Look if the $p_filelist is really an array - if (is_array($p_filelist)) { - - // ----- Look if the first element is also an array - // This will mean that this is a file description entry - if (isset($p_filelist[0]) && is_array($p_filelist[0])) { - $v_att_list = $p_filelist; - } - - // ----- The list is a list of string names - else { - $v_string_list = $p_filelist; - } - } - - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) { - // ----- Create a list from the string - $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); - } - - // ----- Invalid variable type for $p_filelist - else { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); - return 0; - } - - // ----- Reformat the string list - if (sizeof($v_string_list) != 0) { - foreach ($v_string_list as $v_string) { - $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; - } - } - - // ----- For each file in the list check the attributes - $v_supported_attributes - = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' - ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' - ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' - ,PCLZIP_ATT_FILE_MTIME => 'optional' - ,PCLZIP_ATT_FILE_CONTENT => 'optional' - ,PCLZIP_ATT_FILE_COMMENT => 'optional' - ); - foreach ($v_att_list as $v_entry) { - $v_result = $this->privFileDescrParseAtt($v_entry, - $v_filedescr_list[], - $v_options, - $v_supported_attributes); - if ($v_result != 1) { - return 0; - } - } - - // ----- Expand the filelist (expand directories) - $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Call the create fct - $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); - if ($v_result != 1) { - return 0; - } - - // ----- Return - return $p_result_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : listContent() - // Description : - // This public method, gives the list of the files and directories, with their - // properties. - // The properties of each entries in the list are (used also in other functions) : - // filename : Name of the file. For a create or add action it is the filename - // given by the user. For an extract function it is the filename - // of the extracted file. - // stored_filename : Name of the file / directory stored in the archive. - // size : Size of the stored file. - // compressed_size : Size of the file's data compressed in the archive - // (without the headers overhead) - // mtime : Last known modification date of the file (UNIX timestamp) - // comment : Comment associated with the file - // folder : true | false - // index : index of the file in the archive - // status : status of the action (depending of the action) : - // Values are : - // ok : OK ! - // filtered : the file / dir is not extracted (filtered by user) - // already_a_directory : the file can not be extracted because a - // directory with the same name already exists - // write_protected : the file can not be extracted because a file - // with the same name already exists and is - // write protected - // newer_exist : the file was not extracted because a newer file exists - // path_creation_fail : the file is not extracted because the folder - // does not exist and can not be created - // write_error : the file was not extracted because there was a - // error while writing the file - // read_error : the file was not extracted because there was a error - // while reading the file - // invalid_header : the file was not extracted because of an archive - // format error (bad file header) - // Note that each time a method can continue operating when there - // is an action error on a file, the error is only logged in the file status. - // Return Values : - // 0 on an unrecoverable failure, - // The list of the files in the archive. - // -------------------------------------------------------------------------------- - function listContent() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Call the extracting fct - $p_list = array(); - if (($v_result = $this->privList($p_list)) != 1) - { - unset($p_list); - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // extract($p_path="./", $p_remove_path="") - // extract([$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method extract all the files / directories from the archive to the - // folder indicated in $p_path. - // If you want to ignore the 'root' part of path of the memorized files - // you can indicate this in the optional $p_remove_path parameter. - // By default, if a newer file with the same name already exists, the - // file is not extracted. - // - // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions - // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append - // at the end of the path value of PCLZIP_OPT_PATH. - // Parameters : - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 or a negative value on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function extract() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); -// $v_path = "./"; - $v_path = ''; - $v_remove_path = ""; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional' - ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' - ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - )); - if ($v_result != 1) { - return 0; + // ----- Tests the zlib + if (!function_exists('gzopen')) { + die('Abort ' . basename(__FILE__) . ' : Missing zlib extensions'); } - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Trace - - // ----- Call the extracting fct - $p_list = array(); - $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, - $v_remove_all_path, $v_options); - if ($v_result < 1) { - unset($p_list); - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - - // -------------------------------------------------------------------------------- - // Function : - // extractByIndex($p_index, $p_path="./", $p_remove_path="") - // extractByIndex($p_index, [$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method is doing a partial extract of the archive. - // The extracted files or folders are identified by their index in the - // archive (from 0 to n). - // Note that if the index identify a folder, only the folder entry is - // extracted, not all the files included in the archive. - // Parameters : - // $p_index : A single index (integer) or a string of indexes of files to - // extract. The form of the string is "0,4-6,8-12" with only numbers - // and '-' for range or ',' to separate ranges. No spaces or ';' - // are allowed. - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and - // not as files. - // The resulting content is in a new field 'content' in the file - // structure. - // This option must be used alone (any other options are ignored). - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - //function extractByIndex($p_index, options...) - function extractByIndex($p_index) - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); -// $v_path = "./"; - $v_path = ''; - $v_remove_path = ""; - $v_remove_all_path = false; - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_REPLACE_NEWER => 'optional' - ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' - ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', - PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', - PCLZIP_OPT_TEMP_FILE_ON => 'optional', - PCLZIP_OPT_TEMP_FILE_OFF => 'optional' - )); - if ($v_result != 1) { - return 0; - } - - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - } - else { - } - } - - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - - // ----- Get the first argument - $v_path = $v_arg_list[0]; - - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - - // ----- Return - return 0; - } - } - } - - // ----- Trace - - // ----- Trick - // Here I want to reuse extractByRule(), so I need to parse the $p_index - // with privParseOptions() - $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); - $v_options_trick = array(); - $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, - array (PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - return 0; - } - $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; - - // ----- Look for default option values - $this->privOptionDefaultThreshold($v_options); - - // ----- Call the extracting fct - if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { - return(0); - } - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : - // delete([$p_option, $p_option_value, ...]) - // Description : - // This method removes files from the archive. - // If no parameters are given, then all the archive is emptied. - // Parameters : - // None or optional arguments. - // Options : - // PCLZIP_OPT_BY_INDEX : - // PCLZIP_OPT_BY_NAME : - // PCLZIP_OPT_BY_EREG : - // PCLZIP_OPT_BY_PREG : - // Return Values : - // 0 on failure, - // The list of the files which are still present in the archive. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function delete() - { - $v_result=1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Set default values - $v_options = array(); - - // ----- Look for variable options arguments - $v_size = func_num_args(); - - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = func_get_args(); - - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - return 0; - } - } - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Call the delete fct - $v_list = array(); - if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { - $this->privSwapBackMagicQuotes(); - unset($v_list); - return(0); - } - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : deleteByIndex() - // Description : - // ***** Deprecated ***** - // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. - // -------------------------------------------------------------------------------- - function deleteByIndex($p_index) - { - - $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); - - // ----- Return - return $p_list; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : properties() - // Description : - // This method gives the properties of the archive. - // The properties are : - // nb : Number of files in the archive - // comment : Comment associated with the archive file - // status : not_exist, ok - // Parameters : - // None - // Return Values : - // 0 on failure, - // An array with the archive properties. - // -------------------------------------------------------------------------------- - function properties() - { - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - $this->privSwapBackMagicQuotes(); - return(0); - } - - // ----- Default properties - $v_prop = array(); - $v_prop['comment'] = ''; - $v_prop['nb'] = 0; - $v_prop['status'] = 'not_exist'; - - // ----- Look if file exists - if (@is_file($this->zipname)) - { - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + // ----- Set the attributes + $this->zipname = $p_zipname; + $this->zip_fd = 0; + $this->magic_quotes_status = -1; // ----- Return - return 0; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privSwapBackMagicQuotes(); - return 0; - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Set the user attributes - $v_prop['comment'] = $v_central_dir['comment']; - $v_prop['nb'] = $v_central_dir['entries']; - $v_prop['status'] = 'ok'; + return; } + // -------------------------------------------------------------------------------- - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_prop; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : duplicate() - // Description : - // This method creates an archive by copying the content of an other one. If - // the archive already exist, it is replaced by the new one without any warning. - // Parameters : - // $p_archive : The filename of a valid archive, or - // a valid PclZip object. - // Return Values : - // 1 on success. - // 0 or a negative value on error (error code). - // -------------------------------------------------------------------------------- - function duplicate($p_archive) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the $p_archive is a PclZip object - if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + public function create($p_filelist) { - - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive->zipname); - } - - // ----- Look if the $p_archive is a string (so a filename) - else if (is_string($p_archive)) - { - - // ----- Check that $p_archive is a valid zip file - // TBC : Should also check the archive format - if (!is_file($p_archive)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); - $v_result = PCLZIP_ERR_MISSING_FILE; - } - else { - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive); - } - } - - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : merge() - // Description : - // This method merge the $p_archive_to_add archive at the end of the current - // one ($this). - // If the archive ($this) does not exist, the merge becomes a duplicate. - // If the $p_archive_to_add archive does not exist, the merge is a success. - // Parameters : - // $p_archive_to_add : It can be directly the filename of a valid zip archive, - // or a PclZip object archive. - // Return Values : - // 1 on success, - // 0 or negative values on error (see below). - // -------------------------------------------------------------------------------- - function merge($p_archive_to_add) - { - $v_result = 1; - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Check archive - if (!$this->privCheckFormat()) { - return(0); - } - - // ----- Look if the $p_archive_to_add is a PclZip object - if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) - { - - // ----- Merge the archive - $v_result = $this->privMerge($p_archive_to_add); - } - - // ----- Look if the $p_archive_to_add is a string (so a filename) - else if (is_string($p_archive_to_add)) - { - - // ----- Create a temporary archive - $v_object_archive = new PclZip($p_archive_to_add); - - // ----- Merge the archive - $v_result = $this->privMerge($v_object_archive); - } - - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - - - // -------------------------------------------------------------------------------- - // Function : errorCode() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorCode() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorCode()); - } - else { - return($this->error_code); - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorName() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorName($p_with_code=false) - { - $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', - PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', - PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', - PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', - PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', - PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', - PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', - PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', - PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', - PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', - PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', - PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', - PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', - PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', - PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', - PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', - PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', - PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', - PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' - ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' - ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' - ); - - if (isset($v_name[$this->error_code])) { - $v_value = $v_name[$this->error_code]; - } - else { - $v_value = 'NoName'; - } - - if ($p_with_code) { - return($v_value.' ('.$this->error_code.')'); - } - else { - return($v_value); - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : errorInfo() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorInfo($p_full=false) - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorString()); - } - else { - if ($p_full) { - return($this->errorName(true)." : ".$this->error_string); - } - else { - return($this->error_string." [code ".$this->error_code."]"); - } - } - } - // -------------------------------------------------------------------------------- - - -// -------------------------------------------------------------------------------- -// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** -// ***** ***** -// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** -// -------------------------------------------------------------------------------- - - - - // -------------------------------------------------------------------------------- - // Function : privCheckFormat() - // Description : - // This method check that the archive exists and is a valid zip archive. - // Several level of check exists. (futur) - // Parameters : - // $p_level : Level of check. Default 0. - // 0 : Check the first bytes (magic codes) (default value)) - // 1 : 0 + Check the central directory (futur) - // 2 : 1 + Check each file header (futur) - // Return Values : - // true on success, - // false on error, the error code is set. - // -------------------------------------------------------------------------------- - function privCheckFormat($p_level=0) - { - $v_result = true; - - // ----- Reset the file system cache - clearstatcache(); - - // ----- Reset the error handler - $this->privErrorReset(); - - // ----- Look if the file exits - if (!is_file($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); - return(false); - } - - // ----- Check that the file is readeable - if (!is_readable($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); - return(false); - } - - // ----- Check the magic code - // TBC - - // ----- Check the central header - // TBC - - // ----- Check each file header - // TBC - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privParseOptions() - // Description : - // This internal methods reads the variable list of arguments ($p_options_list, - // $p_size) and generate an array with the options and values ($v_result_list). - // $v_requested_options contains the options that can be present and those that - // must be present. - // $v_requested_options is an array, with the option value as key, and 'optional', - // or 'mandatory' as value. - // Parameters : - // See above. - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) - { - $v_result=1; - - // ----- Read the options - $i=0; - while ($i<$p_size) { - - // ----- Check if the option is supported - if (!isset($v_requested_options[$p_options_list[$i]])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for next option - switch ($p_options_list[$i]) { - // ----- Look for options that request a path value - case PCLZIP_OPT_PATH : - case PCLZIP_OPT_REMOVE_PATH : - case PCLZIP_OPT_ADD_PATH : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); - $i++; - break; - - case PCLZIP_OPT_TEMP_FILE_THRESHOLD : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - return PclZip::errorCode(); - } - - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - return PclZip::errorCode(); - } - - // ----- Check the value - $v_value = $p_options_list[$i+1]; - if ((!is_integer($v_value)) || ($v_value<0)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - return PclZip::errorCode(); - } - - // ----- Get the value (and convert it in bytes) - $v_result_list[$p_options_list[$i]] = $v_value*1048576; - $i++; - break; - - case PCLZIP_OPT_TEMP_FILE_ON : - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); - return PclZip::errorCode(); - } - - $v_result_list[$p_options_list[$i]] = true; - break; - - case PCLZIP_OPT_TEMP_FILE_OFF : - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); - return PclZip::errorCode(); - } - // ----- Check for incompatible options - if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); - return PclZip::errorCode(); - } - - $v_result_list[$p_options_list[$i]] = true; - break; - - case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if ( is_string($p_options_list[$i+1]) - && ($p_options_list[$i+1] != '')) { - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); - $i++; - } - else { - } - break; - - // ----- Look for options that request an array of string for value - case PCLZIP_OPT_BY_NAME : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that request an EREG or PREG expression - case PCLZIP_OPT_BY_EREG : - // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG - // to PCLZIP_OPT_BY_PREG - $p_options_list[$i] = PCLZIP_OPT_BY_PREG; - case PCLZIP_OPT_BY_PREG : - //case PCLZIP_OPT_CRYPT : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that takes a string - case PCLZIP_OPT_COMMENT : - case PCLZIP_OPT_ADD_COMMENT : - case PCLZIP_OPT_PREPEND_COMMENT : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, - "Missing parameter value for option '" - .PclZipUtilOptionText($p_options_list[$i]) - ."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, - "Wrong parameter value for option '" - .PclZipUtilOptionText($p_options_list[$i]) - ."'"); - - // ----- Return - return PclZip::errorCode(); - } - $i++; - break; - - // ----- Look for options that request an array of index - case PCLZIP_OPT_BY_INDEX : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_work_list = array(); - if (is_string($p_options_list[$i+1])) { - - // ----- Remove spaces - $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); - - // ----- Parse items - $v_work_list = explode(",", $p_options_list[$i+1]); - } - else if (is_integer($p_options_list[$i+1])) { - $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - $v_work_list = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Reduce the index list - // each index item in the list must be a couple with a start and - // an end value : [0,3], [5-5], [8-10], ... - // ----- Check the format of each item - $v_sort_flag=false; - $v_sort_value=0; - for ($j=0; $j= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - $i++; - break; - - // ----- Look for options that request a call-back - case PCLZIP_CB_PRE_EXTRACT : - case PCLZIP_CB_POST_EXTRACT : - case PCLZIP_CB_PRE_ADD : - case PCLZIP_CB_POST_ADD : - /* for futur use - case PCLZIP_CB_PRE_DELETE : - case PCLZIP_CB_POST_DELETE : - case PCLZIP_CB_PRE_LIST : - case PCLZIP_CB_POST_LIST : - */ - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Get the value - $v_function_name = $p_options_list[$i+1]; - - // ----- Check that the value is a valid existing function - if (!function_exists($v_function_name)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Set the attribute - $v_result_list[$p_options_list[$i]] = $v_function_name; - $i++; - break; - - default : - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Unknown parameter '" - .$p_options_list[$i]."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Next options - $i++; - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($v_result_list[$key])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); - - // ----- Return - return PclZip::errorCode(); - } - } - } - } - - // ----- Look for default values - if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOptionDefaultThreshold() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privOptionDefaultThreshold(&$p_options) - { - $v_result=1; - - if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { - return $v_result; - } - - // ----- Get 'memory_limit' configuration value - $v_memory_limit = ini_get('memory_limit'); - $v_memory_limit = trim($v_memory_limit); - $last = strtolower(substr($v_memory_limit, -1)); - - if($last == 'g') - //$v_memory_limit = $v_memory_limit*1024*1024*1024; - $v_memory_limit = $v_memory_limit*1073741824; - if($last == 'm') - //$v_memory_limit = $v_memory_limit*1024*1024; - $v_memory_limit = $v_memory_limit*1048576; - if($last == 'k') - $v_memory_limit = $v_memory_limit*1024; - - $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); - - - // ----- Sanity check : No threshold if value lower than 1M - if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { - unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrParseAtt() - // Description : - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) - { - $v_result=1; - - // ----- For each file in the list check the attributes - foreach ($p_file_list as $v_key => $v_value) { - - // ----- Check if the option is supported - if (!isset($v_requested_options[$v_key])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for attribute - switch ($v_key) { - case PCLZIP_ATT_FILE_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['filename'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - break; - - case PCLZIP_ATT_FILE_NEW_SHORT_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_short_name'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - break; - - case PCLZIP_ATT_FILE_NEW_FULL_NAME : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); - - if ($p_filedescr['new_full_name'] == '') { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - break; - - // ----- Look for options that takes a string - case PCLZIP_ATT_FILE_COMMENT : - if (!is_string($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['comment'] = $v_value; - break; - - case PCLZIP_ATT_FILE_MTIME : - if (!is_integer($v_value)) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); - return PclZip::errorCode(); - } - - $p_filedescr['mtime'] = $v_value; - break; - - case PCLZIP_ATT_FILE_CONTENT : - $p_filedescr['content'] = $v_value; - break; - - default : - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, - "Unknown parameter '".$v_key."'"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for mandatory options - if ($v_requested_options !== false) { - for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - // ----- Look if present - if (!isset($p_file_list[$key])) { - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); - return PclZip::errorCode(); - } - } - } - } - - // end foreach - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privFileDescrExpand() - // Description : - // This method look for each item of the list to see if its a file, a folder - // or a string to be added as file. For any other type of files (link, other) - // just ignore the item. - // Then prepare the information that will be stored for that file. - // When its a folder, expand the folder with all the files that are in that - // folder (recursively). - // Parameters : - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privFileDescrExpand(&$p_filedescr_list, &$p_options) - { - $v_result=1; - - // ----- Create a result list - $v_result_list = array(); - - // ----- Look each entry - for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); - - // ----- Add the descriptor in result list - $v_result_list[sizeof($v_result_list)] = $v_descr; - - // ----- Look for folder - if ($v_descr['type'] == 'folder') { - // ----- List of items in folder - $v_dirlist_descr = array(); - $v_dirlist_nb = 0; - if ($v_folder_handler = @opendir($v_descr['filename'])) { - while (($v_item_handler = @readdir($v_folder_handler)) !== false) { - - // ----- Skip '.' and '..' - if (($v_item_handler == '.') || ($v_item_handler == '..')) { - continue; - } - - // ----- Compose the full filename - $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; - - // ----- Look for different stored filename - // Because the name of the folder was changed, the name of the - // files/sub-folders also change - if (($v_descr['stored_filename'] != $v_descr['filename']) - && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { - if ($v_descr['stored_filename'] != '') { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; - } - else { - $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; - } - } - - $v_dirlist_nb++; - } - - @closedir($v_folder_handler); - } - else { - // TBC : unable to open folder in read mode - } - - // ----- Expand each element of the list - if ($v_dirlist_nb != 0) { - // ----- Expand - if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { - return $v_result; - } - - // ----- Concat the resulting list - $v_result_list = array_merge($v_result_list, $v_dirlist_descr); - } - else { - } - - // ----- Free local array - unset($v_dirlist_descr); - } - } - - // ----- Get the result list - $p_filedescr_list = $v_result_list; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCreate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privCreate($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the file in write mode - if (($v_result = $this->privOpenFd('wb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Add the list of files - $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); - - // ----- Close - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAdd() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAdd($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Look if the archive exists or is empty - if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) - { - - // ----- Do a create - $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); - - // ----- Return - return $v_result; - } - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) - { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Create the Central Dir files header - for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - $v_count++; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = $v_central_dir['comment']; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { - $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; - } - if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; - } - - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd)-$v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privOpenFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privOpenFd($p_mode) - { - $v_result=1; - - // ----- Look if already open - if ($this->zip_fd != 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCloseFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privCloseFd() - { - $v_result=1; - - if ($this->zip_fd != 0) - @fclose($this->zip_fd); - $this->zip_fd = 0; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddList() - // Description : - // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is - // different from the real path of the file. This is usefull if you want to have PclTar - // running in any directory, and memorize relative path from an other directory. - // Parameters : - // $p_list : An array containing the file or directory names to add in the tar - // $p_result_list : list of added files with their properties (specially the status field) - // $p_add_dir : Path to add in the filename path archived - // $p_remove_dir : Path to remove in the filename path archived - // Return Values : - // -------------------------------------------------------------------------------- -// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - function privAddList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - - // ----- Create the Central Dir files header - for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - // ----- Return - return $v_result; - } - $v_count++; - } - - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; - } - - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd)-$v_offset; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileList() - // Description : - // Parameters : - // $p_filedescr_list : An array containing the file description - // or directory names to add in the zip - // $p_result_list : list of added files with their properties (specially the status field) - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) - { - $v_result=1; - $v_header = array(); - - // ----- Recuperate the current number of elt in list - $v_nb = sizeof($p_result_list); - - // ----- Loop on the files - for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, - $p_options); - if ($v_result != 1) { - return $v_result; - } - - // ----- Store the file infos - $p_result_list[$v_nb++] = $v_header; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFile($p_filedescr, &$p_header, &$p_options) - { - $v_result=1; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - // TBC : Already done in the fileAtt check ... ? - if ($p_filename == "") { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for a stored different filename - /* TBC : Removed - if (isset($p_filedescr['stored_filename'])) { - $v_stored_filename = $p_filedescr['stored_filename']; - } - else { - $v_stored_filename = $p_filedescr['stored_filename']; - } - */ - - // ----- Set the file properties - clearstatcache(); - $p_header['version'] = 20; - $p_header['version_extracted'] = 10; - $p_header['flag'] = 0; - $p_header['compression'] = 0; - $p_header['crc'] = 0; - $p_header['compressed_size'] = 0; - $p_header['filename_len'] = strlen($p_filename); - $p_header['extra_len'] = 0; - $p_header['disk'] = 0; - $p_header['internal'] = 0; - $p_header['offset'] = 0; - $p_header['filename'] = $p_filename; -// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; - $p_header['stored_filename'] = $p_filedescr['stored_filename']; - $p_header['extra'] = ''; - $p_header['status'] = 'ok'; - $p_header['index'] = -1; - - // ----- Look for regular file - if ($p_filedescr['type']=='file') { - $p_header['external'] = 0x00000000; - $p_header['size'] = filesize($p_filename); - } - - // ----- Look for regular folder - else if ($p_filedescr['type']=='folder') { - $p_header['external'] = 0x00000010; - $p_header['mtime'] = filemtime($p_filename); - $p_header['size'] = filesize($p_filename); - } - - // ----- Look for virtual file - else if ($p_filedescr['type'] == 'virtual_file') { - $p_header['external'] = 0x00000000; - $p_header['size'] = strlen($p_filedescr['content']); - } - - - // ----- Look for filetime - if (isset($p_filedescr['mtime'])) { - $p_header['mtime'] = $p_filedescr['mtime']; - } - else if ($p_filedescr['type'] == 'virtual_file') { - $p_header['mtime'] = time(); - } - else { - $p_header['mtime'] = filemtime($p_filename); - } - - // ------ Look for file comment - if (isset($p_filedescr['comment'])) { - $p_header['comment_len'] = strlen($p_filedescr['comment']); - $p_header['comment'] = $p_filedescr['comment']; - } - else { - $p_header['comment_len'] = 0; - $p_header['comment'] = ''; - } - - // ----- Look for pre-add callback - if (isset($p_options[PCLZIP_CB_PRE_ADD])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_header['status'] = "skipped"; $v_result = 1; - } - // ----- Update the informations - // Only some fields can be modified - if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { - $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); - } - } + // ----- Reset the error handler + $this->privErrorReset(); - // ----- Look for empty stored filename - if ($p_header['stored_filename'] == "") { - $p_header['status'] = "filtered"; - } + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = false; - // ----- Check the path length - if (strlen($p_header['stored_filename']) > 0xFF) { - $p_header['status'] = 'filename_too_long'; - } + // ----- Look for variable options arguments + $v_size = func_num_args(); - // ----- Look if no error, or file not skipped - if ($p_header['status'] == 'ok') { + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); - // ----- Look for a file - if ($p_filedescr['type'] == 'file') { - // ----- Look for using temporary file to zip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) - && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) - || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { - $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } + // ----- Remove from the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array( + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + } else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } elseif ($v_size > 2) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + return 0; + } + } } - // ----- Use "in memory" zip algo - else { + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); - // ----- Open the source file - if (($v_file = @fopen($p_filename, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - return PclZip::errorCode(); + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + + // ----- The list is a list of string names + } else { + $v_string_list = $p_filelist; + } + + // ----- Look if the $p_filelist is a string + } elseif (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + + // ----- Invalid variable type for $p_filelist + } else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + + return 0; } - // ----- Read the file content - $v_content = @fread($v_file, $p_header['size']); - - // ----- Close the file - @fclose($v_file); - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + if ($v_string != '') { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } else { + } + } } - // ----- Look for normal compression - else { - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; + // ----- For each file in the list check the attributes + $v_supported_attributes = array( + PCLZIP_ATT_FILE_NAME => 'mandatory', + PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional', + PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional', + PCLZIP_ATT_FILE_MTIME => 'optional', + PCLZIP_ATT_FILE_CONTENT => 'optional', + PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); + if ($v_result != 1) { + return 0; + } } - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - return $v_result; + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; } - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - + // ----- Call the create fct + $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; } - } - - // ----- Look for a virtual file (a file from string) - else if ($p_filedescr['type'] == 'virtual_file') { - - $v_content = $p_filedescr['content']; - - // ----- Calculate the CRC - $p_header['crc'] = @crc32($v_content); - - // ----- Look for no compression - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Set header parameters - $p_header['compressed_size'] = $p_header['size']; - $p_header['compression'] = 0; - } - - // ----- Look for normal compression - else { - // ----- Compress the content - $v_content = @gzdeflate($v_content); - - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content); - $p_header['compression'] = 8; - } - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - return $v_result; - } - - // ----- Write the compressed (or not) content - @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); - } - - // ----- Look for a directory - else if ($p_filedescr['type'] == 'folder') { - // ----- Look for directory last '/' - if (@substr($p_header['stored_filename'], -1) != '/') { - $p_header['stored_filename'] .= '/'; - } - - // ----- Set the file properties - $p_header['size'] = 0; - //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked - $p_header['external'] = 0x00000010; // Value for a folder : to be checked - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) - { - return $v_result; - } - } - } - - // ----- Look for post-add callback - if (isset($p_options[PCLZIP_CB_POST_ADD])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); - if ($v_result == 0) { - // ----- Ignored - $v_result = 1; - } - - // ----- Update the informations - // Nothing can be modified - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privAddFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) - { - $v_result=PCLZIP_ERR_NO_ERROR; - - // ----- Working variable - $p_filename = $p_filedescr['filename']; - - - // ----- Open the source file - if (($v_file = @fopen($p_filename, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - return PclZip::errorCode(); - } - - // ----- Creates a compressed temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; - if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { - fclose($v_file); - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); - return PclZip::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = filesize($p_filename); - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @gzputs($v_file_compressed, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file); - @gzclose($v_file_compressed); - - // ----- Check the minimum file size - if (filesize($v_gzip_temp_name) < 18) { - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); - return PclZip::errorCode(); - } - - // ----- Extract the compressed attributes - if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - // ----- Read the gzip file header - $v_binary_data = @fread($v_file_compressed, 10); - $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); - - // ----- Check some parameters - $v_data_header['os'] = bin2hex($v_data_header['os']); - - // ----- Read the gzip file footer - @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); - $v_binary_data = @fread($v_file_compressed, 8); - $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); - - // ----- Set the attributes - $p_header['compression'] = ord($v_data_header['cm']); - //$p_header['mtime'] = $v_data_header['mtime']; - $p_header['crc'] = $v_data_footer['crc']; - $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - return $v_result; - } - - // ----- Add the compressed data - if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) - { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - fseek($v_file_compressed, 10); - $v_size = $p_header['compressed_size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($v_file_compressed, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close the file - @fclose($v_file_compressed); - - // ----- Unlink the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCalculateStoredFilename() - // Description : - // Based on file descriptor properties and global options, this method - // calculate the filename that will be stored in the archive. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privCalculateStoredFilename(&$p_filedescr, &$p_options) - { - $v_result=1; - - // ----- Working variables - $p_filename = $p_filedescr['filename']; - if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { - $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; - } - else { - $p_add_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { - $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; - } - else { - $p_remove_dir = ''; - } - if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - else { - $p_remove_all_dir = 0; - } - - - // ----- Look for full name change - if (isset($p_filedescr['new_full_name'])) { - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); - } - - // ----- Look for path and/or short name change - else { - - // ----- Look for short name change - // Its when we cahnge just the filename but not the path - if (isset($p_filedescr['new_short_name'])) { - $v_path_info = pathinfo($p_filename); - $v_dir = ''; - if ($v_path_info['dirname'] != '') { - $v_dir = $v_path_info['dirname'].'/'; - } - $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; - } - else { - // ----- Calculate the stored filename - $v_stored_filename = $p_filename; - } - - // ----- Look for all path to remove - if ($p_remove_all_dir) { - $v_stored_filename = basename($p_filename); - } - // ----- Look for partial path remove - else if ($p_remove_dir != "") { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= "/"; - - if ( (substr($p_filename, 0, 2) == "./") - || (substr($p_remove_dir, 0, 2) == "./")) { - - if ( (substr($p_filename, 0, 2) == "./") - && (substr($p_remove_dir, 0, 2) != "./")) { - $p_remove_dir = "./".$p_remove_dir; - } - if ( (substr($p_filename, 0, 2) != "./") - && (substr($p_remove_dir, 0, 2) == "./")) { - $p_remove_dir = substr($p_remove_dir, 2); - } - } - - $v_compare = PclZipUtilPathInclusion($p_remove_dir, - $v_stored_filename); - if ($v_compare > 0) { - if ($v_compare == 2) { - $v_stored_filename = ""; - } - else { - $v_stored_filename = substr($v_stored_filename, - strlen($p_remove_dir)); - } - } - } - - // ----- Remove drive letter if any - $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); - - // ----- Look for path to add - if ($p_add_dir != "") { - if (substr($p_add_dir, -1) == "/") - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir."/".$v_stored_filename; - } - } - - // ----- Filename (reduce the path of stored name) - $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); - $p_filedescr['stored_filename'] = $v_stored_filename; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteFileHeader(&$p_header) - { - $v_result=1; - - // ----- Store the offset position of the file - $p_header['offset'] = ftell($this->zip_fd); - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - - // ----- Packed data - $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, - $p_header['version_extracted'], $p_header['flag'], - $p_header['compression'], $v_mtime, $v_mdate, - $p_header['crc'], $p_header['compressed_size'], - $p_header['size'], - strlen($p_header['stored_filename']), - $p_header['extra_len']); - - // ----- Write the first 148 bytes of the header in the archive - fputs($this->zip_fd, $v_binary_data, 30); - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralFileHeader(&$p_header) - { - $v_result=1; - - // TBC - //for(reset($p_header); $key = key($p_header); next($p_header)) { - //} - - // ----- Transform UNIX mtime to DOS format mdate/mtime - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - - - // ----- Packed data - $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, - $p_header['version'], $p_header['version_extracted'], - $p_header['flag'], $p_header['compression'], - $v_mtime, $v_mdate, $p_header['crc'], - $p_header['compressed_size'], $p_header['size'], - strlen($p_header['stored_filename']), - $p_header['extra_len'], $p_header['comment_len'], - $p_header['disk'], $p_header['internal'], - $p_header['external'], $p_header['offset']); - - // ----- Write the 42 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 46); - - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - if ($p_header['comment_len'] != 0) - { - fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privWriteCentralHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) - { - $v_result=1; - - // ----- Packed data - $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, - $p_nb_entries, $p_size, - $p_offset, strlen($p_comment)); - - // ----- Write the 22 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 22); - - // ----- Write the variable fields - if (strlen($p_comment) != 0) - { - fputs($this->zip_fd, $p_comment, strlen($p_comment)); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privList() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privList(&$p_list) - { - $v_result=1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Open the zip file - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Go to beginning of Central Dir - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_central_dir['offset'])) - { - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read each entry - for ($i=0; $i<$v_central_dir['entries']; $i++) - { - // ----- Read the file header - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - $v_header['index'] = $i; - - // ----- Get the only interesting attributes - $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); - unset($v_header); - } - - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Magic quotes trick - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privConvertHeader2FileInfo() - // Description : - // This function takes the file informations from the central directory - // entries and extract the interesting parameters that will be given back. - // The resulting file infos are set in the array $p_info - // $p_info['filename'] : Filename with full path. Given by user (add), - // extracted in the filesystem (extract). - // $p_info['stored_filename'] : Stored filename in the archive. - // $p_info['size'] = Size of the file. - // $p_info['compressed_size'] = Compressed size of the file. - // $p_info['mtime'] = Last modification date of the file. - // $p_info['comment'] = Comment associated with the file. - // $p_info['folder'] = true/false : indicates if the entry is a folder or not. - // $p_info['status'] = status of the action on the file. - // $p_info['crc'] = CRC of the file content. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privConvertHeader2FileInfo($p_header, &$p_info) - { - $v_result=1; - - // ----- Get the interesting attributes - $v_temp_path = PclZipUtilPathReduction($p_header['filename']); - $p_info['filename'] = $v_temp_path; - $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); - $p_info['stored_filename'] = $v_temp_path; - $p_info['size'] = $p_header['size']; - $p_info['compressed_size'] = $p_header['compressed_size']; - $p_info['mtime'] = $p_header['mtime']; - $p_info['comment'] = $p_header['comment']; - $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); - $p_info['index'] = $p_header['index']; - $p_info['status'] = $p_header['status']; - $p_info['crc'] = $p_header['crc']; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractByRule() - // Description : - // Extract a file or directory depending of rules (by index, by name, ...) - // Parameters : - // $p_file_list : An array where will be placed the properties of each - // extracted file - // $p_path : Path to add while writing the extracted files - // $p_remove_path : Path to remove (from the file memorized path) while writing the - // extracted files. If the path does not match the file path, - // the file is extracted with its memorized path. - // $p_remove_path does not apply to 'list' mode. - // $p_path and $p_remove_path are commulative. - // Return Values : - // 1 on success,0 or less on error (see error code list) - // -------------------------------------------------------------------------------- - function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result=1; - - // ----- Magic quotes trick - $this->privDisableMagicQuotes(); - - // ----- Check the path - if ( ($p_path == "") - || ( (substr($p_path, 0, 1) != "/") - && (substr($p_path, 0, 3) != "../") - && (substr($p_path,1,2)!=":/"))) - $p_path = "./".$p_path; - - // ----- Reduce the path last (and duplicated) '/' - if (($p_path != "./") && ($p_path != "/")) - { - // ----- Look for the path end '/' - while (substr($p_path, -1) == "/") - { - $p_path = substr($p_path, 0, strlen($p_path)-1); - } - } - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) - { - $p_remove_path .= '/'; - } - $p_remove_path_size = strlen($p_remove_path); - - // ----- Open the zip file - if (($v_result = $this->privOpenFd('rb')) != 1) - { - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - - // ----- Read each entry - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - - // ----- Read next Central dir entry - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Store the index - $v_header['index'] = $i; - - // ----- Store the file position - $v_pos_entry = ftell($this->zip_fd); - - // ----- Look for the specific extract rules - $v_extract = false; - - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_extract = true; - } - } - // ----- Look for a filename - elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - $v_extract = true; - } - } - } - - // ----- Look for extract by ereg rule - // ereg() is deprecated with PHP 5.3 - /* - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { - $v_extract = true; - } - } - */ - - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { - $v_extract = true; - } - } - - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_extract = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j+1; - } - - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - break; - } - } - } - - // ----- Look for no rule, which means extract all the archive - else { - $v_extract = true; - } - - // ----- Check compression method - if ( ($v_extract) - && ( ($v_header['compression'] != 8) - && ($v_header['compression'] != 0))) { - $v_header['status'] = 'unsupported_compression'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, - "Filename '".$v_header['stored_filename']."' is " - ."compressed by an unsupported compression " - ."method (".$v_header['compression'].") "); - - return PclZip::errorCode(); - } - } - - // ----- Check encrypted files - if (($v_extract) && (($v_header['flag'] & 1) == 1)) { - $v_header['status'] = 'unsupported_encryption'; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - $this->privSwapBackMagicQuotes(); - - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, - "Unsupported encryption for " - ." filename '".$v_header['stored_filename'] - ."'"); - - return PclZip::errorCode(); - } + return $p_result_list; } + // -------------------------------------------------------------------------------- - // ----- Look for real extraction - if (($v_extract) && ($v_header['status'] != 'ok')) { - $v_result = $this->privConvertHeader2FileInfo($v_header, - $p_file_list[$v_nb_extracted++]); - if ($v_result != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - $v_extract = false; - } - - // ----- Look for real extraction - if ($v_extract) - { - - // ----- Go to the file position - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_header['offset'])) - { - // ----- Close the zip file - $this->privCloseFd(); - - $this->privSwapBackMagicQuotes(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Look for extraction as string - if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { - - $v_string = ''; - - // ----- Extracting the file - $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Set the file content - $p_file_list[$v_nb_extracted]['content'] = $v_string; - - // ----- Next extracted file - $v_nb_extracted++; - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - // ----- Look for extraction in standard output - elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) - && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { - // ----- Extracting the file in standard output - $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - // ----- Look for normal extraction - else { - // ----- Extracting the file - $v_result1 = $this->privExtractFile($v_header, - $p_path, $p_remove_path, - $p_remove_all_path, - $p_options); - if ($v_result1 < 1) { - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - return $v_result1; - } - - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - return $v_result; - } - - // ----- Look for user callback abort - if ($v_result1 == 2) { - break; - } - } - } - } - - // ----- Close the zip file - $this->privCloseFd(); - $this->privSwapBackMagicQuotes(); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFile() - // Description : - // Parameters : - // Return Values : - // - // 1 : ... ? - // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback - // -------------------------------------------------------------------------------- - function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - $v_result=1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_OPT_ADD_COMMENT : + // PCLZIP_OPT_PREPEND_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + public function add($p_filelist) { - // ----- Return - return $v_result; - } + $v_result = 1; + // ----- Reset the error handler + $this->privErrorReset(); - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = false; - // ----- Look for all path to remove - if ($p_remove_all_path == true) { - // ----- Look for folder entry that not need to be extracted - if (($p_entry['external']&0x00000010)==0x00000010) { + // ----- Look for variable options arguments + $v_size = func_num_args(); - $p_entry['status'] = "filtered"; + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); - return $v_result; + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array( + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_ADD_COMMENT => 'optional', + PCLZIP_OPT_PREPEND_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + } else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } elseif ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } } - // ----- Get the basename of the path - $p_entry['filename'] = basename($p_entry['filename']); - } + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); - // ----- Look for path to remove - else if ($p_remove_path != "") - { - if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) - { + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); - // ----- Change the file status - $p_entry['status'] = "filtered"; + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + + // ----- The list is a list of string names + } else { + $v_string_list = $p_filelist; + } + + // ----- Look if the $p_filelist is a string + } elseif (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + + // ----- Invalid variable type for $p_filelist + } else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '" . gettype($p_filelist) . "' for p_filelist"); + + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes = array( + PCLZIP_ATT_FILE_NAME => 'mandatory', + PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional', + PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional', + PCLZIP_ATT_FILE_MTIME => 'optional', + PCLZIP_ATT_FILE_CONTENT => 'optional', + PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } // ----- Return - return $v_result; - } - - $p_remove_path_size = strlen($p_remove_path); - if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) - { - - // ----- Remove the path - $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); - - } + return $p_result_list; } + // -------------------------------------------------------------------------------- - // ----- Add the path - if ($p_path != '') { - $p_entry['filename'] = $p_path."/".$p_entry['filename']; - } - - // ----- Check a base_dir_restriction - if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { - $v_inclusion - = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], - $p_entry['filename']); - if ($v_inclusion == 0) { - - PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, - "Filename '".$p_entry['filename']."' is " - ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); - - return PclZip::errorCode(); - } - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; - $v_result = 1; - } - - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } - - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Look for specific actions while the file exist - if (file_exists($p_entry['filename'])) + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exist and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + public function listContent() { - - // ----- Look if file is a directory - if (is_dir($p_entry['filename'])) - { - - // ----- Change the file status - $p_entry['status'] = "already_a_directory"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, - "Filename '".$p_entry['filename']."' is " - ."already used by an existing directory"); - - return PclZip::errorCode(); - } - } - // ----- Look if file is write protected - else if (!is_writeable($p_entry['filename'])) - { - - // ----- Change the file status - $p_entry['status'] = "write_protected"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, - "Filename '".$p_entry['filename']."' exists " - ."and is write protected"); - - return PclZip::errorCode(); - } - } - - // ----- Look if the extracted file is older - else if (filemtime($p_entry['filename']) > $p_entry['mtime']) - { - // ----- Change the file status - if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) - && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { - } - else { - $p_entry['status'] = "newer_exist"; - - // ----- Look for PCLZIP_OPT_STOP_ON_ERROR - // For historical reason first PclZip implementation does not stop - // when this kind of error occurs. - if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) - && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { - - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, - "Newer version of '".$p_entry['filename']."' exists " - ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); - - return PclZip::errorCode(); - } - } - } - else { - } - } - - // ----- Check the directory availability and create it if necessary - else { - if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) - $v_dir_to_check = $p_entry['filename']; - else if (!strstr($p_entry['filename'], "/")) - $v_dir_to_check = ""; - else - $v_dir_to_check = dirname($p_entry['filename']); - - if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { - - // ----- Change the file status - $p_entry['status'] = "path_creation_fail"; - - // ----- Return - //return $v_result; - $v_result = 1; - } - } - } - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) - { - // ----- Look for not compressed file - if ($p_entry['compression'] == 0) { - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) - { - - // ----- Change the file status - $p_entry['status'] = "write_error"; - - // ----- Return - return $v_result; - } - - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - /* Try to speed up the code - $v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_binary_data, $v_read_size); - */ - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Closing the destination file - fclose($v_dest_file); - - // ----- Change the file mtime - touch($p_entry['filename'], $p_entry['mtime']); - - - } - else { - // ----- TBC - // Need to be finished - if (($p_entry['flag'] & 1) == 1) { - PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); - return PclZip::errorCode(); - } - - - // ----- Look for using temporary file to unzip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) - && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) - || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) - && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { - $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); - if ($v_result < PCLZIP_ERR_NO_ERROR) { - return $v_result; - } - } - - // ----- Look for extract in memory - else { - - - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Decompress the file - $v_file_content = @gzinflate($v_buffer); - unset($v_buffer); - if ($v_file_content === FALSE) { - - // ----- Change the file status - // TBC - $p_entry['status'] = "error"; - - return $v_result; - } - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - - // ----- Change the file status - $p_entry['status'] = "write_error"; - - return $v_result; - } - - // ----- Write the uncompressed data - @fwrite($v_dest_file, $v_file_content, $p_entry['size']); - unset($v_file_content); - - // ----- Closing the destination file - @fclose($v_dest_file); - - } - - // ----- Change the file mtime - @touch($p_entry['filename'], $p_entry['mtime']); - } - - // ----- Look for chmod option - if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { - - // ----- Change the mode of the file - @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); - } - - } - } - - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileUsingTempFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileUsingTempFile(&$p_entry, &$p_options) - { - $v_result=1; - - // ----- Creates a temporary file - $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; - if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { - fclose($v_file); - PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); - return PclZip::errorCode(); - } - - - // ----- Write gz file format header - $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); - @fwrite($v_dest_file, $v_binary_data, 10); - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Write gz file format footer - $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); - @fwrite($v_dest_file, $v_binary_data, 8); - - // ----- Close the temporary file - @fclose($v_dest_file); - - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - $p_entry['status'] = "write_error"; - return $v_result; - } - - // ----- Open the temporary gz file - if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { - @fclose($v_dest_file); - $p_entry['status'] = "read_error"; - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); - return PclZip::errorCode(); - } - - - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['size']; - while ($v_size != 0) { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($v_src_file, $v_read_size); - //$v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - @fclose($v_dest_file); - @gzclose($v_src_file); - - // ----- Delete the temporary file - @unlink($v_gzip_temp_name); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileInOutput() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileInOutput(&$p_entry, &$p_options) - { - $v_result=1; - - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) { - return $v_result; - } - - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; $v_result = 1; - } - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } + // ----- Reset the error handler + $this->privErrorReset(); - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - // ----- Trace - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) { - // ----- Look for not compressed file - if ($p_entry['compressed_size'] == $p_entry['size']) { - - // ----- Read the file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - - // ----- Send the file to the output - echo $v_buffer; - unset($v_buffer); + // ----- Check archive + if (!$this->privCheckFormat()) { + return (0); } - else { - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) { + unset($p_list); - // ----- Decompress the file - $v_file_content = gzinflate($v_buffer); - unset($v_buffer); - - // ----- Send the file to the output - echo $v_file_content; - unset($v_file_content); + return (0); } - } + + // ----- Return + return $p_list; } + // -------------------------------------------------------------------------------- - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privExtractFileAsString() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) - { - $v_result=1; - - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadFileHeader($v_header)) != 1) + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + public function extract() { - // ----- Return - return $v_result; - } - - - // ----- Check that the file header is coherent with $p_entry info - if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { - // TBC - } - - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; $v_result = 1; - } - // ----- Look for abort result - if ($v_result == 2) { - // ----- This status is internal and will be changed in 'skipped' - $p_entry['status'] = "aborted"; - $v_result = PCLZIP_ERR_USER_ABORTED; - } + // ----- Reset the error handler + $this->privErrorReset(); - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - } - - - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) { - // ----- Look for not compressed file - // if ($p_entry['compressed_size'] == $p_entry['size']) - if ($p_entry['compression'] == 0) { - - // ----- Reading the file - $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + // ----- Check archive + if (!$this->privCheckFormat()) { + return (0); } - else { - // ----- Reading the file - $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + // ----- Set default values + $v_options = array(); + // $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; - // ----- Decompress the file - if (($p_string = @gzinflate($v_data)) === FALSE) { - // TBC - } + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array( + PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional', + PCLZIP_OPT_STOP_ON_ERROR => 'optional', + PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + } else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } elseif ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Trace + + // ----- Call the extracting fct + $p_list = array(); + $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options); + if ($v_result < 1) { + unset($p_list); + + return (0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extractByIndex($p_index, options...) + public function extractByIndex($p_index) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return (0); + } + + // ----- Set default values + $v_options = array(); + // $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array( + PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional', + PCLZIP_OPT_STOP_ON_ERROR => 'optional', + PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = false; + } else { + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + } else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } elseif ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } } // ----- Trace - } - else { - // TBC : error : can not extract a folder in a string - } - } - - // ----- Change abort status - if ($p_entry['status'] == "aborted") { - $p_entry['status'] = "skipped"; - } - - // ----- Look for post-extract callback - elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - - // ----- Swap the content to header - $v_local_header['content'] = $p_string; - $p_string = ''; - - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. -// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); - - // ----- Swap back the content to header - $p_string = $v_local_header['content']; - unset($v_local_header['content']); - - // ----- Look for abort result - if ($v_result == 2) { - $v_result = PCLZIP_ERR_USER_ABORTED; - } - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadFileHeader(&$p_header) - { - $v_result=1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x04034b50) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 26); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 26) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); - - // ----- Get filename - $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); - - // ----- Get extra_fields - if ($v_data['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); - } - else { - $p_header['extra'] = ''; - } - - // ----- Extract properties - $p_header['version_extracted'] = $v_data['version']; - $p_header['compression'] = $v_data['compression']; - $p_header['size'] = $v_data['size']; - $p_header['compressed_size'] = $v_data['compressed_size']; - $p_header['crc'] = $v_data['crc']; - $p_header['flag'] = $v_data['flag']; - $p_header['filename_len'] = $v_data['filename_len']; - - // ----- Recuperate date in UNIX format - $p_header['mdate'] = $v_data['mdate']; - $p_header['mtime'] = $v_data['mtime']; - if ($p_header['mdate'] && $p_header['mtime']) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - - } - else - { - $p_header['mtime'] = time(); - } - - // TBC - //for(reset($v_data); $key = key($v_data); next($v_data)) { - //} - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set the status field - $p_header['status'] = "ok"; - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadCentralFileHeader(&$p_header) - { - $v_result=1; - - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] != 0x02014b50) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 42); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 42) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); - - // ----- Get filename - if ($p_header['filename_len'] != 0) - $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); - else - $p_header['filename'] = ''; - - // ----- Get extra - if ($p_header['extra_len'] != 0) - $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); - else - $p_header['extra'] = ''; - - // ----- Get comment - if ($p_header['comment_len'] != 0) - $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); - else - $p_header['comment'] = ''; - - // ----- Extract properties - - // ----- Recuperate date in UNIX format - //if ($p_header['mdate'] && $p_header['mtime']) - // TBC : bug : this was ignoring time with 0/0/0 - if (1) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - - // ----- Get UNIX date format - $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - - } - else - { - $p_header['mtime'] = time(); - } - - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - - // ----- Set default status to ok - $p_header['status'] = 'ok'; - - // ----- Look if it is a directory - if (substr($p_header['filename'], -1) == '/') { - //$p_header['external'] = 0x41FF0010; - $p_header['external'] = 0x00000010; - } - - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privCheckFileHeaders() - // Description : - // Parameters : - // Return Values : - // 1 on success, - // 0 on error; - // -------------------------------------------------------------------------------- - function privCheckFileHeaders(&$p_local_header, &$p_central_header) - { - $v_result=1; - - // ----- Check the static values - // TBC - if ($p_local_header['filename'] != $p_central_header['filename']) { - } - if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { - } - if ($p_local_header['flag'] != $p_central_header['flag']) { - } - if ($p_local_header['compression'] != $p_central_header['compression']) { - } - if ($p_local_header['mtime'] != $p_central_header['mtime']) { - } - if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { - } - - // ----- Look for flag bit 3 - if (($p_local_header['flag'] & 8) == 8) { - $p_local_header['size'] = $p_central_header['size']; - $p_local_header['compressed_size'] = $p_central_header['compressed_size']; - $p_local_header['crc'] = $p_central_header['crc']; - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privReadEndCentralDir() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadEndCentralDir(&$p_central_dir) - { - $v_result=1; - - // ----- Go to the end of the zip file - $v_size = filesize($this->zipname); - @fseek($this->zip_fd, $v_size); - if (@ftell($this->zip_fd) != $v_size) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- First try : look if this is an archive with no commentaries (most of the time) - // in this case the end of central dir is at 22 bytes of the file end - $v_found = 0; - if ($v_size > 26) { - @fseek($this->zip_fd, $v_size-22); - if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read for bytes - $v_binary_data = @fread($this->zip_fd, 4); - $v_data = @unpack('Vid', $v_binary_data); - - // ----- Check signature - if ($v_data['id'] == 0x06054b50) { - $v_found = 1; - } - - $v_pos = ftell($this->zip_fd); - } - - // ----- Go back to the maximum possible size of the Central Dir End Record - if (!$v_found) { - $v_maximum_size = 65557; // 0xFFFF + 22; - if ($v_maximum_size > $v_size) - $v_maximum_size = $v_size; - @fseek($this->zip_fd, $v_size-$v_maximum_size); - if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read byte per byte in order to find the signature - $v_pos = ftell($this->zip_fd); - $v_bytes = 0x00000000; - while ($v_pos < $v_size) - { - // ----- Read a byte - $v_byte = @fread($this->zip_fd, 1); - - // ----- Add the byte - //$v_bytes = ($v_bytes << 8) | Ord($v_byte); - // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number - // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. - $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); - - // ----- Compare the bytes - if ($v_bytes == 0x504b0506) - { - $v_pos++; - break; + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array( + PCLZIP_OPT_BY_INDEX, + $p_index + ); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, array( + PCLZIP_OPT_BY_INDEX => 'optional' + )); + if ($v_result != 1) { + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { + return (0); } - $v_pos++; - } - - // ----- Look if not found end of central dir - if ($v_pos == $v_size) - { - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); - // ----- Return - return PclZip::errorCode(); - } + return $p_list; } + // -------------------------------------------------------------------------------- - // ----- Read the first 18 bytes of the header - $v_binary_data = fread($this->zip_fd, 18); - - // ----- Look for invalid block size - if (strlen($v_binary_data) != 18) + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // This method removes files from the archive. + // If no parameters are given, then all the archive is emptied. + // Parameters : + // None or optional arguments. + // Options : + // PCLZIP_OPT_BY_INDEX : + // PCLZIP_OPT_BY_NAME : + // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_PREG : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + public function delete() { + $v_result = 1; - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + // ----- Reset the error handler + $this->privErrorReset(); - // ----- Return - return PclZip::errorCode(); - } - - // ----- Extract the values - $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); - - // ----- Check the global size - if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { - - // ----- Removed in release 2.2 see readme file - // The check of the file size is a little too strict. - // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. - // While decrypted, zip has training 0 bytes - if (0) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, - 'The central dir is not at the end of the archive.' - .' Some trailing bytes exists after the archive.'); - - // ----- Return - return PclZip::errorCode(); - } - } - - // ----- Get comment - if ($v_data['comment_size'] != 0) { - $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); - } - else - $p_central_dir['comment'] = ''; - - $p_central_dir['entries'] = $v_data['entries']; - $p_central_dir['disk_entries'] = $v_data['disk_entries']; - $p_central_dir['offset'] = $v_data['offset']; - $p_central_dir['size'] = $v_data['size']; - $p_central_dir['disk'] = $v_data['disk']; - $p_central_dir['disk_start'] = $v_data['disk_start']; - - // TBC - //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { - //} - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDeleteByRule() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDeleteByRule(&$p_result_list, &$p_options) - { - $v_result=1; - $v_list_detail = array(); - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Scan all the files - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - @rewind($this->zip_fd); - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Read each entry - $v_header_list = array(); - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - - // ----- Read the file header - $v_header_list[$v_nb_extracted] = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - - return $v_result; - } - - - // ----- Store the index - $v_header_list[$v_nb_extracted]['index'] = $i; - - // ----- Look for the specific extract rules - $v_found = false; - - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } - elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ - && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - $v_found = true; - } - } - // ----- Look for a filename - elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - $v_found = true; - } - } - } - - // ----- Look for extract by ereg rule - // ereg() is deprecated with PHP 5.3 - /* - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - $v_found = true; - } - } - */ - - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - $v_found = true; - } - } - - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - $v_found = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - $j_start = $j+1; - } - - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - break; - } - } - } - else { - $v_found = true; - } - - // ----- Look for deletion - if ($v_found) - { - unset($v_header_list[$v_nb_extracted]); - } - else - { - $v_nb_extracted++; - } - } - - // ----- Look if something need to be deleted - if ($v_nb_extracted > 0) { - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Creates a temporary zip archive - $v_temp_zip = new PclZip($v_zip_temp_name); - - // ----- Open the temporary zip file in write mode - if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { - $this->privCloseFd(); - - // ----- Return - return $v_result; + // ----- Check archive + if (!$this->privCheckFormat()) { + return (0); } - // ----- Look which file need to be kept - for ($i=0; $izip_fd); - if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array( + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { + $this->privSwapBackMagicQuotes(); + unset($v_list); + + return (0); + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + public function deleteByIndex($p_index) + { + + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + public function properties() + { + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + $this->privSwapBackMagicQuotes(); + + return (0); + } + + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + + // ----- Look if file exists + if (@is_file($this->zipname)) { + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { + $this->privSwapBackMagicQuotes(); // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in binary read mode'); + + // ----- Return + return 0; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + $this->privSwapBackMagicQuotes(); + + return 0; + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_prop; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + public function duplicate($p_archive) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) { + + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + + // ----- Look if the $p_archive is a string (so a filename) + } elseif (is_string($p_archive)) { + + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '" . $p_archive . "'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + + // ----- Invalid variable + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + public function merge($p_archive_to_add) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return (0); + } + + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) { + + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + + // ----- Look if the $p_archive_to_add is a string (so a filename) + } elseif (is_string($p_archive_to_add)) { + + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + + // ----- Invalid variable + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return (PclErrorCode()); + } + + return ($this->error_code); + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function errorName($p_with_code = false) + { + $v_name = array( + PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', + PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', + PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', + PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', + PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' + ); + + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } else { + $v_value = 'NoName'; + } + + if ($p_with_code) { + return ($v_value . ' (' . $this->error_code . ')'); + } + + return ($v_value); + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function errorInfo($p_full = false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return (PclErrorString()); + } + + if ($p_full) { + return ($this->errorName(true) . " : " . $this->error_string); + } + + return ($this->error_string . " [code " . $this->error_code . "]"); + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** + // ***** ***** + // ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + public function privCheckFormat($p_level = 0) + { + $v_result = true; + + // ----- Reset the file system cache + clearstatcache(); + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '" . $this->zipname . "'"); + + return (false); + } + + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '" . $this->zipname . "'"); + + return (false); + } + + // ----- Check the magic code + // TBC + + // ----- Check the central header + // TBC + + // ----- Check each file header + // TBC + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + public function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options = false) + { + $v_result = 1; + + // ----- Read the options + $i = 0; + while ($i < $p_size) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '" . $p_options_list[$i] . "' for this method"); // ----- Return return PclZip::errorCode(); } - // ----- Read the file header - $v_local_header = array(); - if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH: + case PCLZIP_OPT_REMOVE_PATH: + case PCLZIP_OPT_ADD_PATH: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); - // ----- Return - return $v_result; + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i + 1], false); + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_THRESHOLD: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + return PclZip::errorCode(); + } + + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + + return PclZip::errorCode(); + } + + // ----- Check the value + $v_value = $p_options_list[$i + 1]; + if ((!is_integer($v_value)) || ($v_value < 0)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + return PclZip::errorCode(); + } + + // ----- Get the value (and convert it in bytes) + $v_result_list[$p_options_list[$i]] = $v_value * 1048576; + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_ON: + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_TEMP_FILE_OFF: + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); + + return PclZip::errorCode(); + } + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '" . PclZipUtilOptionText($p_options_list[$i]) . "' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); + + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i + 1]) && ($p_options_list[$i + 1] != '')) { + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i + 1], false); + $i++; + } else { + } + break; + + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i + 1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i + 1]; + } elseif (is_array($p_options_list[$i + 1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG: + $p_options_list[$i] = PCLZIP_OPT_BY_PREG; + // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG + // to PCLZIP_OPT_BY_PREG + case PCLZIP_OPT_BY_PREG: + //case PCLZIP_OPT_CRYPT : + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i + 1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that takes a string + case PCLZIP_OPT_COMMENT: + case PCLZIP_OPT_ADD_COMMENT: + case PCLZIP_OPT_PREPEND_COMMENT: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i + 1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i + 1])) { + + // ----- Remove spaces + $p_options_list[$i + 1] = strtr($p_options_list[$i + 1], ' ', ''); + + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i + 1]); + } elseif (is_integer($p_options_list[$i + 1])) { + $v_work_list[0] = $p_options_list[$i + 1] . '-' . $p_options_list[$i + 1]; + } elseif (is_array($p_options_list[$i + 1])) { + $v_work_list = $p_options_list[$i + 1]; + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag = false; + $v_sort_value = 0; + for ($j = 0; $j < sizeof($v_work_list); $j++) { + // ----- Explode the item + $v_item_list = explode("-", $v_work_list[$j]); + $v_size_item_list = sizeof($v_item_list); + + // ----- TBC : Here we might check that each item is a + // real integer ... + + // ----- Look for single value + if ($v_size_item_list == 1) { + // ----- Set the option value + $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; + $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[0]; + } elseif ($v_size_item_list == 2) { + // ----- Set the option value + $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; + $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[1]; + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Too many values in index range for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for list sort + if ($v_result_list[$p_options_list[$i]][$j]['start'] < $v_sort_value) { + $v_sort_flag = true; + + // ----- TBC : An automatic sort should be writen ... + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Invalid order of index range for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start']; + } + + // ----- Sort the items + if ($v_sort_flag) { + // TBC : To Be Completed + } + + // ----- Next option + $i++; + break; + + // ----- Look for options that request no value + case PCLZIP_OPT_REMOVE_ALL_PATH: + case PCLZIP_OPT_EXTRACT_AS_STRING: + case PCLZIP_OPT_NO_COMPRESSION: + case PCLZIP_OPT_EXTRACT_IN_OUTPUT: + case PCLZIP_OPT_REPLACE_NEWER: + case PCLZIP_OPT_STOP_ON_ERROR: + $v_result_list[$p_options_list[$i]] = true; + break; + + // ----- Look for options that request an octal value + case PCLZIP_OPT_SET_CHMOD: + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i + 1]; + $i++; + break; + + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT: + case PCLZIP_CB_POST_EXTRACT: + case PCLZIP_CB_PRE_ADD: + case PCLZIP_CB_POST_ADD: + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i + 1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_function_name = $p_options_list[$i + 1]; + + // ----- Check that the value is a valid existing function + if (!function_exists($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '" . $v_function_name . "()' is not an existing function for option '" . PclZipUtilOptionText($p_options_list[$i]) . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + + default: + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '" . $p_options_list[$i] . "'"); + + // ----- Return + return PclZip::errorCode(); } - // ----- Check that local file header is same as central file header - if ($this->privCheckFileHeaders($v_local_header, - $v_header_list[$i]) != 1) { - // TBC - } - unset($v_local_header); + // ----- Next options + $i++; + } - // ----- Write the file header - if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key = reset($v_requested_options); $key = key($v_requested_options); $key = next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter " . PclZipUtilOptionText($key) . "(" . $key . ")"); - // ----- Return - return $v_result; - } - - // ----- Read/write the data block - if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - - // ----- Return - return $v_result; + // ----- Return + return PclZip::errorCode(); + } + } } } - // ----- Store the offset of the central dir - $v_offset = @ftell($v_temp_zip->zip_fd); + // ----- Look for default values + if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - // ----- Re-Create the Central Dir files header - for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOptionDefaultThreshold() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privOptionDefaultThreshold(&$p_options) + { + $v_result = 1; + + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { + return $v_result; + } + + // ----- Get 'memory_limit' configuration value + $v_memory_limit = ini_get('memory_limit'); + $v_memory_limit = trim($v_memory_limit); + $last = strtolower(substr($v_memory_limit, -1)); + $v_memory_limit = preg_replace('/[^0-9,.]/', '', $v_memory_limit); + + if ($last == 'g') { + //$v_memory_limit = $v_memory_limit*1024*1024*1024; + $v_memory_limit = $v_memory_limit * 1073741824; + } + if ($last == 'm') { + //$v_memory_limit = $v_memory_limit*1024*1024; + $v_memory_limit = $v_memory_limit * 1048576; + } + if ($last == 'k') { + $v_memory_limit = $v_memory_limit * 1024; + } + + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit * PCLZIP_TEMPORARY_FILE_RATIO); + + // ----- Sanity check : No threshold if value lower than 1M + if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { + unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrParseAtt() + // Description : + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + public function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options = false) + { + $v_result = 1; + + // ----- For each file in the list check the attributes + foreach ($p_file_list as $v_key => $v_value) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$v_key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '" . $v_key . "' for this file"); // ----- Return - return $v_result; + return PclZip::errorCode(); } - // ----- Transform the header to a 'usable' info - $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + // ----- Look for attribute + switch ($v_key) { + case PCLZIP_ATT_FILE_NAME: + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type " . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['filename'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + break; + + case PCLZIP_ATT_FILE_NEW_SHORT_NAME: + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type " . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_short_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + break; + + case PCLZIP_ATT_FILE_NEW_FULL_NAME: + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type " . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_full_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + break; + + // ----- Look for options that takes a string + case PCLZIP_ATT_FILE_COMMENT: + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type " . gettype($v_value) . ". String expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + $p_filedescr['comment'] = $v_value; + break; + + case PCLZIP_ATT_FILE_MTIME: + if (!is_integer($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type " . gettype($v_value) . ". Integer expected for attribute '" . PclZipUtilOptionText($v_key) . "'"); + + return PclZip::errorCode(); + } + + $p_filedescr['mtime'] = $v_value; + break; + + case PCLZIP_ATT_FILE_CONTENT: + $p_filedescr['content'] = $v_value; + break; + + default: + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '" . $v_key . "'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key = reset($v_requested_options); $key = key($v_requested_options); $key = next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($p_file_list[$key])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter " . PclZipUtilOptionText($key) . "(" . $key . ")"); + + return PclZip::errorCode(); + } + } + } + } + + // end foreach } + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- - // ----- Zip file comment - $v_comment = ''; - if (isset($p_options[PCLZIP_OPT_COMMENT])) { - $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + // -------------------------------------------------------------------------------- + // Function : privFileDescrExpand() + // Description : + // This method look for each item of the list to see if its a file, a folder + // or a string to be added as file. For any other type of files (link, other) + // just ignore the item. + // Then prepare the information that will be stored for that file. + // When its a folder, expand the folder with all the files that are in that + // folder (recursively). + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + public function privFileDescrExpand(&$p_filedescr_list, &$p_options) + { + $v_result = 1; + + // ----- Create a result list + $v_result_list = array(); + + // ----- Look each entry + for ($i = 0; $i < sizeof($p_filedescr_list); $i++) { + + // ----- Get filedescr + $v_descr = $p_filedescr_list[$i]; + + // ----- Reduce the filename + $v_descr['filename'] = PclZipUtilTranslateWinPath($v_descr['filename'], false); + $v_descr['filename'] = PclZipUtilPathReduction($v_descr['filename']); + + // ----- Look for real file or folder + if (file_exists($v_descr['filename'])) { + if (@is_file($v_descr['filename'])) { + $v_descr['type'] = 'file'; + } elseif (@is_dir($v_descr['filename'])) { + $v_descr['type'] = 'folder'; + } elseif (@is_link($v_descr['filename'])) { + // skip + continue; + } else { + // skip + continue; + } + + // ----- Look for string added as file + } elseif (isset($v_descr['content'])) { + $v_descr['type'] = 'virtual_file'; + + // ----- Missing file + } else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '" . $v_descr['filename'] . "' does not exist"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Calculate the stored filename + $this->privCalculateStoredFilename($v_descr, $p_options); + + // ----- Add the descriptor in result list + $v_result_list[sizeof($v_result_list)] = $v_descr; + + // ----- Look for folder + if ($v_descr['type'] == 'folder') { + // ----- List of items in folder + $v_dirlist_descr = array(); + $v_dirlist_nb = 0; + if ($v_folder_handler = @opendir($v_descr['filename'])) { + while (($v_item_handler = @readdir($v_folder_handler)) !== false) { + + // ----- Skip '.' and '..' + if (($v_item_handler == '.') || ($v_item_handler == '..')) { + continue; + } + + // ----- Compose the full filename + $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'] . '/' . $v_item_handler; + + // ----- Look for different stored filename + // Because the name of the folder was changed, the name of the + // files/sub-folders also change + if (($v_descr['stored_filename'] != $v_descr['filename']) && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + if ($v_descr['stored_filename'] != '') { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'] . '/' . $v_item_handler; + } else { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; + } + } + + $v_dirlist_nb++; + } + + @closedir($v_folder_handler); + } else { + // TBC : unable to open folder in read mode + } + + // ----- Expand each element of the list + if ($v_dirlist_nb != 0) { + // ----- Expand + if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { + return $v_result; + } + + // ----- Concat the resulting list + $v_result_list = array_merge($v_result_list, $v_dirlist_descr); + } else { + } + + // ----- Free local array + unset($v_dirlist_descr); + } } - // ----- Calculate the size of the central header - $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + // ----- Get the result list + $p_filedescr_list = $v_result_list; - // ----- Create the central dir footer - if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privCreate($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result = 1; + $v_list_detail = array(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) { + // ----- Return + return $v_result; + } + + // ----- Add the list of files + $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); + + // ----- Close + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privAdd($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result = 1; + $v_list_detail = array(); + + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) { + + // ----- Do a create + $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); + + // ----- Return + return $v_result; + } + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_zip_temp_name . '\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Create the Central Dir files header + for ($i = 0, $v_count = 0; $i < sizeof($v_header_list); $i++) { + // ----- Create the file header + if ($v_header_list[$i]['status'] == 'ok') { + if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = $v_central_dir['comment']; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { + $v_comment = $v_comment . $p_options[PCLZIP_OPT_ADD_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT] . $v_comment; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd) - $v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count + $v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + // ----- Close - $v_temp_zip->privCloseFd(); $this->privCloseFd(); + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + // ----- Delete the zip file // TBC : I should test the result ... @unlink($this->zipname); @@ -4954,544 +2269,2964 @@ //@rename($v_zip_temp_name, $this->zipname); PclZipUtilRename($v_zip_temp_name, $this->zipname); - // ----- Destroy the temporary archive - unset($v_temp_zip); + // ----- Return + return $v_result; } + // -------------------------------------------------------------------------------- - // ----- Remove every files : reset the file - else if ($v_central_dir['entries'] != 0) { + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function privOpenFd($p_mode) + { + $v_result = 1; + + // ----- Look if already open + if ($this->zip_fd != 0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \'' . $this->zipname . '\' already open'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in ' . $p_mode . ' mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function privCloseFd() + { + $v_result = 1; + + if ($this->zip_fd != 0) { + @fclose($this->zip_fd); + } + $this->zip_fd = 0; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- + // function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + public function privAddList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result = 1; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Create the Central Dir files header + for ($i = 0, $v_count = 0; $i < sizeof($v_header_list); $i++) { + // ----- Create the file header + if ($v_header_list[$i]['status'] == 'ok') { + if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd) - $v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // Parameters : + // $p_filedescr_list : An array containing the file description + // or directory names to add in the zip + // $p_result_list : list of added files with their properties (specially the status field) + // Return Values : + // -------------------------------------------------------------------------------- + public function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result = 1; + $v_header = array(); + + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + + // ----- Loop on the files + for ($j = 0; ($j < sizeof($p_filedescr_list)) && ($v_result == 1); $j++) { + // ----- Format the filename + $p_filedescr_list[$j]['filename'] = PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false); + + // ----- Skip empty file names + // TBC : Can this be possible ? not checked in DescrParseAtt ? + if ($p_filedescr_list[$j]['filename'] == "") { + continue; + } + + // ----- Check the filename + if (($p_filedescr_list[$j]['type'] != 'virtual_file') && (!file_exists($p_filedescr_list[$j]['filename']))) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '" . $p_filedescr_list[$j]['filename'] . "' does not exist"); + + return PclZip::errorCode(); + } + + // ----- Look if it is a file or a dir with no all path remove option + // or a dir with all its path removed + // if ( (is_file($p_filedescr_list[$j]['filename'])) + // || ( is_dir($p_filedescr_list[$j]['filename']) + if (($p_filedescr_list[$j]['type'] == 'file') || ($p_filedescr_list[$j]['type'] == 'virtual_file') || (($p_filedescr_list[$j]['type'] == 'folder') && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]) || !$p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + + // ----- Add the file + $v_result = $this->privAddFile($p_filedescr_list[$j], $v_header, $p_options); + if ($v_result != 1) { + return $v_result; + } + + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privAddFile($p_filedescr, &$p_header, &$p_options) + { + $v_result = 1; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // TBC : Already done in the fileAtt check ... ? + if ($p_filename == "") { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for a stored different filename + /* TBC : Removed + if (isset($p_filedescr['stored_filename'])) { + $v_stored_filename = $p_filedescr['stored_filename']; + } else { + $v_stored_filename = $p_filedescr['stored_filename']; + } + */ + + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; + // TBC : Removed $p_header['stored_filename'] = $v_stored_filename; + $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['extra'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + + // ----- Look for regular file + if ($p_filedescr['type'] == 'file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = filesize($p_filename); + + // ----- Look for regular folder + } elseif ($p_filedescr['type'] == 'folder') { + $p_header['external'] = 0x00000010; + $p_header['mtime'] = filemtime($p_filename); + $p_header['size'] = filesize($p_filename); + + // ----- Look for virtual file + } elseif ($p_filedescr['type'] == 'virtual_file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = strlen($p_filedescr['content']); + } + + // ----- Look for filetime + if (isset($p_filedescr['mtime'])) { + $p_header['mtime'] = $p_filedescr['mtime']; + } elseif ($p_filedescr['type'] == 'virtual_file') { + $p_header['mtime'] = time(); + } else { + $p_header['mtime'] = filemtime($p_filename); + } + + // ------ Look for file comment + if (isset($p_filedescr['comment'])) { + $p_header['comment_len'] = strlen($p_filedescr['comment']); + $p_header['comment'] = $p_filedescr['comment']; + } else { + $p_header['comment_len'] = 0; + $p_header['comment'] = ''; + } + + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + } + } + + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + + // ----- Look for a file + if ($p_filedescr['type'] == 'file') { + // ----- Look for using temporary file to zip + if ((!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])))) { + $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + + // ----- Use "in memory" zip algo + } else { + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + + return PclZip::errorCode(); + } + + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + + // ----- Close the file + @fclose($v_file); + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + + // ----- Look for normal compression + } else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + } + + // ----- Look for a virtual file (a file from string) + } elseif ($p_filedescr['type'] == 'virtual_file') { + + $v_content = $p_filedescr['content']; + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + + // ----- Look for normal compression + } else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + // ----- Look for a directory + } elseif ($p_filedescr['type'] == 'folder') { + // ----- Look for directory last '/' + if (@substr($p_header['stored_filename'], -1) != '/') { + $p_header['stored_filename'] .= '/'; + } + + // ----- Set the file properties + $p_header['size'] = 0; + //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + $p_header['external'] = 0x00000010; // Value for a folder : to be checked + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + } + } + + // ----- Look for post-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + + // ----- Update the informations + // Nothing can be modified + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) + { + $v_result = PCLZIP_ERR_NO_ERROR; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + + return PclZip::errorCode(); + } + + // ----- Creates a compressed temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.gz'; + if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary write mode'); + + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = filesize($p_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @gzputs($v_file_compressed, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file); + @gzclose($v_file_compressed); + + // ----- Check the minimum file size + if (filesize($v_gzip_temp_name) < 18) { + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \'' . $v_gzip_temp_name . '\' has invalid filesize - should be minimum 18 bytes'); + + return PclZip::errorCode(); + } + + // ----- Extract the compressed attributes + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); + + return PclZip::errorCode(); + } + + // ----- Read the gzip file header + $v_binary_data = @fread($v_file_compressed, 10); + $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); + + // ----- Check some parameters + $v_data_header['os'] = bin2hex($v_data_header['os']); + + // ----- Read the gzip file footer + @fseek($v_file_compressed, filesize($v_gzip_temp_name) - 8); + $v_binary_data = @fread($v_file_compressed, 8); + $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); + + // ----- Set the attributes + $p_header['compression'] = ord($v_data_header['cm']); + //$p_header['mtime'] = $v_data_header['mtime']; + $p_header['crc'] = $v_data_footer['crc']; + $p_header['compressed_size'] = filesize($v_gzip_temp_name) - 18; + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + + // ----- Add the compressed data + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); + + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + fseek($v_file_compressed, 10); + $v_size = $p_header['compressed_size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file_compressed, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Unlink the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCalculateStoredFilename() + // Description : + // Based on file descriptor properties and global options, this method + // calculate the filename that will be stored in the archive. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privCalculateStoredFilename(&$p_filedescr, &$p_options) + { + $v_result = 1; + + // ----- Working variables + $p_filename = $p_filedescr['filename']; + if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { + $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; + } else { + $p_add_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { + $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; + } else { + $p_remove_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } else { + $p_remove_all_dir = 0; + } + + // ----- Look for full name change + if (isset($p_filedescr['new_full_name'])) { + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); + + // ----- Look for path and/or short name change + } else { + + // ----- Look for short name change + // Its when we cahnge just the filename but not the path + if (isset($p_filedescr['new_short_name'])) { + $v_path_info = pathinfo($p_filename); + $v_dir = ''; + if ($v_path_info['dirname'] != '') { + $v_dir = $v_path_info['dirname'] . '/'; + } + $v_stored_filename = $v_dir . $p_filedescr['new_short_name']; + } else { + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + } + + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + + // ----- Look for partial path remove + } elseif ($p_remove_dir != "") { + if (substr($p_remove_dir, -1) != '/') { + $p_remove_dir .= "/"; + } + + if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) { + + if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) { + $p_remove_dir = "./" . $p_remove_dir; + } + if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) { + $p_remove_dir = substr($p_remove_dir, 2); + } + } + + $v_compare = PclZipUtilPathInclusion($p_remove_dir, $v_stored_filename); + if ($v_compare > 0) { + if ($v_compare == 2) { + $v_stored_filename = ""; + } else { + $v_stored_filename = substr($v_stored_filename, strlen($p_remove_dir)); + } + } + } + + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); + + // ----- Look for path to add + if ($p_add_dir != "") { + if (substr($p_add_dir, -1) == "/") { + $v_stored_filename = $p_add_dir . $v_stored_filename; + } else { + $v_stored_filename = $p_add_dir . "/" . $v_stored_filename; + } + } + } + + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + $p_filedescr['stored_filename'] = $v_stored_filename; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privWriteFileHeader(&$p_header) + { + $v_result = 1; + + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours'] << 11) + ($v_date['minutes'] << 5) + $v_date['seconds'] / 2; + $v_mdate = (($v_date['year'] - 1980) << 9) + ($v_date['mon'] << 5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len']); + + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privWriteCentralFileHeader(&$p_header) + { + $v_result = 1; + + // TBC + //for (reset($p_header); $key = key($p_header); next($p_header)) { + //} + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours'] << 11) + ($v_date['minutes'] << 5) + $v_date['seconds'] / 2; + $v_mdate = (($v_date['year'] - 1980) << 9) + ($v_date['mon'] << 5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'], $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); + + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + $v_result = 1; + + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); + + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + + // ----- Write the variable fields + if (strlen($p_comment) != 0) { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privList(&$p_list) + { + $v_result = 1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \'' . $this->zipname . '\' in binary read mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Go to beginning of Central Dir + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + for ($i = 0; $i < $v_central_dir['entries']; $i++) { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + $v_header['index'] = $i; + + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + + // ----- Close the zip file $this->privCloseFd(); + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // $p_info['crc'] = CRC of the file content. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privConvertHeader2FileInfo($p_header, &$p_info) + { + $v_result = 1; + + // ----- Get the interesting attributes + $v_temp_path = PclZipUtilPathReduction($p_header['filename']); + $p_info['filename'] = $v_temp_path; + $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); + $p_info['stored_filename'] = $v_temp_path; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external'] & 0x00000010) == 0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + $p_info['crc'] = $p_header['crc']; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + public function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result = 1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check the path + if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path, 1, 2) != ":/"))) { + $p_path = "./" . $p_path; + } + + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") { + $p_path = substr($p_path, 0, strlen($p_path) - 1); + } + } + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) { + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + + // ----- Read each entry + $j_start = 0; + for ($i = 0, $v_nb_extracted = 0; $i < $v_central_dir['entries']; $i++) { + + // ----- Read next Central dir entry + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Store the index + $v_header['index'] = $i; + + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + + // ----- Look for the specific extract rules + $v_extract = false; + + // ----- Look for extract by name rule + if ((isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j = 0; ($j < sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_extract); $j++) { + + // ----- Look for a directory + if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") { + + // ----- Look if the directory is in the filename path + if ((strlen($v_header['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_extract = true; + } + + // ----- Look for a filename + } elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_extract = true; + } + } + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + elseif ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + */ + + // ----- Look for extract by preg rule + } elseif ((isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + $v_extract = true; + } + + // ----- Look for extract by index rule + } elseif ((isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j = $j_start; ($j < sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_extract); $j++) { + + if (($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i <= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_extract = true; + } + if ($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j + 1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start'] > $i) { + break; + } + } + + // ----- Look for no rule, which means extract all the archive + } else { + $v_extract = true; + } + + // ----- Check compression method + if (($v_extract) && (($v_header['compression'] != 8) && ($v_header['compression'] != 0))) { + $v_header['status'] = 'unsupported_compression'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, "Filename '" . $v_header['stored_filename'] . "' is " . "compressed by an unsupported compression " . "method (" . $v_header['compression'] . ") "); + + return PclZip::errorCode(); + } + } + + // ----- Check encrypted files + if (($v_extract) && (($v_header['flag'] & 1) == 1)) { + $v_header['status'] = 'unsupported_encryption'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, "Unsupported encryption for " . " filename '" . $v_header['stored_filename'] . "'"); + + return PclZip::errorCode(); + } + } + + // ----- Look for real extraction + if (($v_extract) && ($v_header['status'] != 'ok')) { + $v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++]); + if ($v_result != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + $v_extract = false; + } + + // ----- Look for real extraction + if ($v_extract) { + + // ----- Go to the file position + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + + $v_string = ''; + + // ----- Extracting the file + $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + + // ----- Next extracted file + $v_nb_extracted++; + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + + // ----- Look for extraction in standard output + } elseif ((isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { + // ----- Extracting the file in standard output + $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + + // ----- Look for normal extraction + } else { + // ----- Extracting the file + $v_result1 = $this->privExtractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + } + } + + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // + // 1 : ... ? + // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback + // -------------------------------------------------------------------------------- + public function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result = 1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + // ----- Return + return $v_result; + } + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Look for folder entry that not need to be extracted + if (($p_entry['external'] & 0x00000010) == 0x00000010) { + + $p_entry['status'] = "filtered"; + + return $v_result; + } + + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + + // ----- Look for path to remove + } elseif ($p_remove_path != "") { + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) { + + // ----- Change the file status + $p_entry['status'] = "filtered"; + + // ----- Return + return $v_result; + } + + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) { + + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + + } + } + + // ----- Add the path + if ($p_path != '') { + $p_entry['filename'] = $p_path . "/" . $p_entry['filename']; + } + + // ----- Check a base_dir_restriction + if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { + $v_inclusion = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], $p_entry['filename']); + if ($v_inclusion == 0) { + + PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, "Filename '" . $p_entry['filename'] . "' is " . "outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); + + return PclZip::errorCode(); + } + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) { + + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) { + + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { + + PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, "Filename '" . $p_entry['filename'] . "' is " . "already used by an existing directory"); + + return PclZip::errorCode(); + } + + // ----- Look if file is write protected + } elseif (!is_writeable($p_entry['filename'])) { + + // ----- Change the file status + $p_entry['status'] = "write_protected"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Filename '" . $p_entry['filename'] . "' exists " . "and is write protected"); + + return PclZip::errorCode(); + } + + // ----- Look if the extracted file is older + } elseif (filemtime($p_entry['filename']) > $p_entry['mtime']) { + // ----- Change the file status + if ((isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) && ($p_options[PCLZIP_OPT_REPLACE_NEWER] === true)) { + } else { + $p_entry['status'] = "newer_exist"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ((isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR] === true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Newer version of '" . $p_entry['filename'] . "' exists " . "and option PCLZIP_OPT_REPLACE_NEWER is not selected"); + + return PclZip::errorCode(); + } + } + } else { + } + + // ----- Check the directory availability and create it if necessary + } else { + if ((($p_entry['external'] & 0x00000010) == 0x00000010) || (substr($p_entry['filename'], -1) == '/')) { + $v_dir_to_check = $p_entry['filename']; + } elseif (!strstr($p_entry['filename'], "/")) { + $v_dir_to_check = ""; + } else { + $v_dir_to_check = dirname($p_entry['filename']); + } + + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external'] & 0x00000010) == 0x00000010))) != 1) { + + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + + // ----- Return + //return $v_result; + $v_result = 1; + } + } + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compression'] == 0) { + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + // ----- Return + return $v_result; + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + /* Try to speed up the code + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + */ + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Closing the destination file + fclose($v_dest_file); + + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + + } else { + // ----- TBC + // Need to be finished + if (($p_entry['flag'] & 1) == 1) { + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \'' . $p_entry['filename'] . '\' is encrypted. Encrypted files are not supported.'); + + return PclZip::errorCode(); + } + + // ----- Look for using temporary file to unzip + if ((!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])))) { + $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + + // ----- Look for extract in memory + } else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = @gzinflate($v_buffer); + unset($v_buffer); + if ($v_file_content === false) { + + // ----- Change the file status + // TBC + $p_entry['status'] = "error"; + + return $v_result; + } + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + + // ----- Closing the destination file + @fclose($v_dest_file); + + } + + // ----- Change the file mtime + @touch($p_entry['filename'], $p_entry['mtime']); + } + + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + + // ----- Change the mode of the file + @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + + // ----- Look for post-extract callback + } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privExtractFileUsingTempFile(&$p_entry, &$p_options) + { + $v_result = 1; + + // ----- Creates a temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.gz'; + if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary write mode'); + + return PclZip::errorCode(); + } + + // ----- Write gz file format header + $v_binary_data = pack('va1a1Va1a1', 0x8b1f, chr($p_entry['compression']), chr(0x00), time(), chr(0x00), chr(3)); + @fwrite($v_dest_file, $v_binary_data, 10); + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Write gz file format footer + $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); + @fwrite($v_dest_file, $v_binary_data, 8); + + // ----- Close the temporary file + @fclose($v_dest_file); + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Open the temporary gz file + if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { + @fclose($v_dest_file); + $p_entry['status'] = "read_error"; + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_gzip_temp_name . '\' in binary read mode'); + + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($v_src_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + @fclose($v_dest_file); + @gzclose($v_src_file); + + // ----- Delete the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileInOutput() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privExtractFileInOutput(&$p_entry, &$p_options) + { + $v_result = 1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + return $v_result; + } + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Trace + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) { + + // ----- Read the file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Send the file to the output + echo $v_buffer; + unset($v_buffer); + } else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + + // ----- Send the file to the output + echo $v_file_content; + unset($v_file_content); + } + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + + // ----- Look for post-extract callback + } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) + { + $v_result = 1; + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + // ----- Return + return $v_result; + } + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external'] & 0x00000010) == 0x00000010)) { + // ----- Look for not compressed file + // if ($p_entry['compressed_size'] == $p_entry['size']) + if ($p_entry['compression'] == 0) { + + // ----- Reading the file + $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + } else { + + // ----- Reading the file + $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + if (($p_string = @gzinflate($v_data)) === false) { + // TBC + } + } + + // ----- Trace + } else { + // TBC : error : can not extract a folder in a string + } + + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + + // ----- Look for post-extract callback + } elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Swap the content to header + $v_local_header['content'] = $p_string; + $p_string = ''; + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + // eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Swap back the content to header + $p_string = $v_local_header['content']; + unset($v_local_header['content']); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privReadFileHeader(&$p_header) + { + $v_result = 1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x04034b50) { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : " . strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + + // ----- Get filename + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + + // ----- Get extra_fields + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } else { + $p_header['extra'] = ''; + } + + // ----- Extract properties + $p_header['version_extracted'] = $v_data['version']; + $p_header['compression'] = $v_data['compression']; + $p_header['size'] = $v_data['size']; + $p_header['compressed_size'] = $v_data['compressed_size']; + $p_header['crc'] = $v_data['crc']; + $p_header['flag'] = $v_data['flag']; + $p_header['filename_len'] = $v_data['filename_len']; + + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F) * 2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } else { + $p_header['mtime'] = time(); + } + + // TBC + //for (reset($v_data); $key = key($v_data); next($v_data)) { + //} + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set the status field + $p_header['status'] = "ok"; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privReadCentralFileHeader(&$p_header) + { + $v_result = 1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x02014b50) { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : " . strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + + // ----- Get filename + if ($p_header['filename_len'] != 0) { + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + } else { + $p_header['filename'] = ''; + } + + // ----- Get extra + if ($p_header['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + } else { + $p_header['extra'] = ''; + } + + // ----- Get comment + if ($p_header['comment_len'] != 0) { + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + } else { + $p_header['comment'] = ''; + } + + // ----- Extract properties + + // ----- Recuperate date in UNIX format + //if ($p_header['mdate'] && $p_header['mtime']) + // TBC : bug : this was ignoring time with 0/0/0 + if (1) { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F) * 2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } else { + $p_header['mtime'] = time(); + } + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set default status to ok + $p_header['status'] = 'ok'; + + // ----- Look if it is a directory + if (substr($p_header['filename'], -1) == '/') { + //$p_header['external'] = 0x41FF0010; + $p_header['external'] = 0x00000010; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFileHeaders() + // Description : + // Parameters : + // Return Values : + // 1 on success, + // 0 on error; + // -------------------------------------------------------------------------------- + public function privCheckFileHeaders(&$p_local_header, &$p_central_header) + { + $v_result = 1; + + // ----- Check the static values + // TBC + if ($p_local_header['filename'] != $p_central_header['filename']) { + } + if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { + } + if ($p_local_header['flag'] != $p_central_header['flag']) { + } + if ($p_local_header['compression'] != $p_central_header['compression']) { + } + if ($p_local_header['mtime'] != $p_central_header['mtime']) { + } + if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { + } + + // ----- Look for flag bit 3 + if (($p_local_header['flag'] & 8) == 8) { + $p_local_header['size'] = $p_central_header['size']; + $p_local_header['compressed_size'] = $p_central_header['compressed_size']; + $p_local_header['crc'] = $p_central_header['crc']; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privReadEndCentralDir(&$p_central_dir) + { + $v_result = 1; + + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + @fseek($this->zip_fd, $v_size); + if (@ftell($this->zip_fd) != $v_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \'' . $this->zipname . '\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + @fseek($this->zip_fd, $v_size - 22); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size - 22)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \'' . $this->zipname . '\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = @unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + $v_found = 1; + } + + $v_pos = ftell($this->zip_fd); + } + + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) { + $v_maximum_size = $v_size; + } + @fseek($this->zip_fd, $v_size - $v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size - $v_maximum_size)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \'' . $this->zipname . '\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + + // ----- Add the byte + //$v_bytes = ($v_bytes << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = (($v_bytes & 0xFFFFFF) << 8) | ord($v_byte); + + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) { + $v_pos++; + break; + } + + $v_pos++; + } + + // ----- Look if not found end of central dir + if ($v_pos == $v_size) { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : " . strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + + // ----- Check the global size + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { + + // ----- Removed in release 2.2 see readme file + // The check of the file size is a little too strict. + // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. + // While decrypted, zip has training 0 bytes + if (0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'The central dir is not at the end of the archive.' . ' Some trailing bytes exists after the archive.'); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Get comment + if ($v_data['comment_size'] != 0) { + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + } else { + $p_central_dir['comment'] = ''; + } + + $p_central_dir['entries'] = $v_data['entries']; + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + $p_central_dir['offset'] = $v_data['offset']; + $p_central_dir['size'] = $v_data['size']; + $p_central_dir['disk'] = $v_data['disk']; + $p_central_dir['disk_start'] = $v_data['disk_start']; + + // TBC + //for (reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + //} + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privDeleteByRule(&$p_result_list, &$p_options) + { + $v_result = 1; + $v_list_detail = array(); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + $this->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) { + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i = 0, $v_nb_extracted = 0; $i < $v_central_dir['entries']; $i++) { + + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + + return $v_result; + } + + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + + // ----- Look for the specific extract rules + $v_found = false; + + // ----- Look for extract by name rule + if ((isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j = 0; ($j < sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_found); $j++) { + + // ----- Look for a directory + if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") { + + // ----- Look if the directory is in the filename path + if ((strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } elseif ((($v_header_list[$v_nb_extracted]['external'] & 0x00000010) == 0x00000010) /* Indicates a folder */ && ($v_header_list[$v_nb_extracted]['stored_filename'] . '/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + + // ----- Look for a filename + } elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_found = true; + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + elseif ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + */ + + // ----- Look for extract by preg rule + } elseif ((isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + + // ----- Look for extract by index rule + } elseif ((isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j = $j_start; ($j < sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_found); $j++) { + + if (($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i <= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_found = true; + } + if ($i >= $p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j + 1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start'] > $i) { + break; + } + } + } else { + $v_found = true; + } + + // ----- Look for deletion + if ($v_found) { + unset($v_header_list[$v_nb_extracted]); + } else { + $v_nb_extracted++; + } + } + + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; + + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + + // ----- Open the temporary zip file in write mode + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Look which file need to be kept + for ($i = 0; $i < sizeof($v_header_list); $i++) { + + // ----- Calculate the position of the header + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_local_header = array(); + if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Check that local file header is same as central file header + if ($this->privCheckFileHeaders($v_local_header, $v_header_list[$i]) != 1) { + // TBC + } + unset($v_local_header); + + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + + // ----- Re-Create the Central Dir files header + for ($i = 0; $i < sizeof($v_header_list); $i++) { + // ----- Create the file header + if (($v_result = $v_temp_zip->privWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd) - $v_offset; + + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Destroy the temporary archive + unset($v_temp_zip); + + // ----- Remove every files : reset the file + } elseif ($v_central_dir['entries'] != 0) { + $this->privCloseFd(); + + if (($v_result = $this->privOpenFd('wb')) != 1) { + return $v_result; + } + + if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { + return $v_result; + } + + $this->privCloseFd(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + public function privDirCheck($p_dir, $p_is_dir = false) + { + $v_result = 1; + + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1) == '/')) { + $p_dir = substr($p_dir, 0, strlen($p_dir) - 1); + } + + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) { + return 1; + } + + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + + // ----- Just a check + if ($p_parent_dir != $p_dir) { + // ----- Look for parent directory + if ($p_parent_dir != "") { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) { + return $v_result; + } + } + } + + // ----- Create the directory + if (!@mkdir($p_dir, 0777)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privMerge(&$p_archive_to_add) + { + $v_result = 1; + + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) { + + // ----- Nothing to merge, so merge is a success + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Look if the archive exists + if (!is_file($this->zipname)) { + + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { + $this->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Open the archive_to_add file + if (($v_result = $p_archive_to_add->privOpenFd('rb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($p_archive_to_add->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR . uniqid('pclzip-') . '.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \'' . $v_zip_temp_name . '\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Merge the file comments + $v_comment = $v_central_dir['comment'] . ' ' . $v_central_dir_to_add['comment']; + + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd) - $v_offset; + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries'] + $v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privDuplicate($p_archive_filename) + { + $v_result = 1; + + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) { + + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Open the zip file if (($v_result = $this->privOpenFd('wb')) != 1) { - return $v_result; + // ----- Return + return $v_result; } - if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { - return $v_result; + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) { + $this->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \'' . $p_archive_filename . '\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); } + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Return + return $v_result; } + // -------------------------------------------------------------------------------- - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDirCheck() - // Description : - // Check if a directory exists, if not it creates it and all the parents directory - // which may be useful. - // Parameters : - // $p_dir : Directory path to check. - // Return Values : - // 1 : OK - // -1 : Unable to create directory - // -------------------------------------------------------------------------------- - function privDirCheck($p_dir, $p_is_dir=false) - { - $v_result = 1; - - - // ----- Remove the final '/' - if (($p_is_dir) && (substr($p_dir, -1)=='/')) + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function privErrorLog($p_error_code = 0, $p_error_string = '') { - $p_dir = substr($p_dir, 0, strlen($p_dir)-1); - } - - // ----- Check the directory availability - if ((is_dir($p_dir)) || ($p_dir == "")) - { - return 1; - } - - // ----- Extract parent directory - $p_parent_dir = dirname($p_dir); - - // ----- Just a check - if ($p_parent_dir != $p_dir) - { - // ----- Look for parent directory - if ($p_parent_dir != "") - { - if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) - { - return $v_result; + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; } - } } + // -------------------------------------------------------------------------------- - // ----- Create the directory - if (!@mkdir($p_dir, 0777)) + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + public function privErrorReset() { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); - - // ----- Return - return PclZip::errorCode(); + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } else { + $this->error_code = 0; + $this->error_string = ''; + } } + // -------------------------------------------------------------------------------- - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privMerge() - // Description : - // If $p_archive_to_add does not exist, the function exit with a success result. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privMerge(&$p_archive_to_add) - { - $v_result=1; - - // ----- Look if the archive_to_add exists - if (!is_file($p_archive_to_add->zipname)) + // -------------------------------------------------------------------------------- + // Function : privDisableMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privDisableMagicQuotes() { + $v_result = 1; - // ----- Nothing to merge, so merge is a success - $v_result = 1; + // ----- Look if function exists + if ((!function_exists("get_magic_quotes_runtime")) || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } - // ----- Return - return $v_result; + // ----- Look if already done + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Get and memorize the magic_quote value + $this->magic_quotes_status = @get_magic_quotes_runtime(); + + // ----- Disable magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime(0); + } + + // ----- Return + return $v_result; } + // -------------------------------------------------------------------------------- - // ----- Look if the archive exists - if (!is_file($this->zipname)) + // -------------------------------------------------------------------------------- + // Function : privSwapBackMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + public function privSwapBackMagicQuotes() { + $v_result = 1; - // ----- Do a duplicate - $v_result = $this->privDuplicate($p_archive_to_add->zipname); + // ----- Look if function exists + if ((!function_exists("get_magic_quotes_runtime")) || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } - // ----- Return - return $v_result; + // ----- Look if something to do + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Swap back magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime($this->magic_quotes_status); + } + + // ----- Return + return $v_result; } + // -------------------------------------------------------------------------------- +} - // ----- Open the zip file - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - return $v_result; - } +// End of class +// -------------------------------------------------------------------------------- - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - return $v_result; - } - - // ----- Go to beginning of File - @rewind($this->zip_fd); - - // ----- Open the archive_to_add file - if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) - { - $this->privCloseFd(); - - // ----- Return - return $v_result; - } - - // ----- Read the central directory informations - $v_central_dir_to_add = array(); - if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - return $v_result; - } - - // ----- Go to beginning of File - @rewind($p_archive_to_add->zip_fd); - - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the files from the archive_to_add into the temporary file - $v_size = $v_central_dir_to_add['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Store the offset of the central dir - $v_offset = @ftell($v_zip_temp_fd); - - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Copy the block of file headers from the archive_to_add - $v_size = $v_central_dir_to_add['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Merge the file comments - $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; - - // ----- Calculate the size of the (new) central header - $v_size = @ftell($v_zip_temp_fd)-$v_offset; - - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive fd - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - @fclose($v_zip_temp_fd); - $this->zip_fd = null; - - // ----- Reset the file list - unset($v_header_list); - - // ----- Return - return $v_result; - } - - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - - // ----- Close - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDuplicate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDuplicate($p_archive_filename) - { - $v_result=1; - - // ----- Look if the $p_archive_filename exists - if (!is_file($p_archive_filename)) - { - - // ----- Nothing to duplicate, so duplicate is a success. - $v_result = 1; - - // ----- Return - return $v_result; - } - - // ----- Open the zip file - if (($v_result=$this->privOpenFd('wb')) != 1) - { - // ----- Return - return $v_result; - } - - // ----- Open the temporary file in write mode - if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) - { - $this->privCloseFd(); - - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); - - // ----- Return - return PclZip::errorCode(); - } - - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = filesize($p_archive_filename); - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - - // ----- Close - $this->privCloseFd(); - - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorLog() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorLog($p_error_code=0, $p_error_string='') - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclError($p_error_code, $p_error_string); - } - else { - $this->error_code = $p_error_code; - $this->error_string = $p_error_string; - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privErrorReset() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorReset() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclErrorReset(); - } - else { - $this->error_code = 0; - $this->error_string = ''; - } - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privDisableMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDisableMagicQuotes() - { - $v_result=1; - - // ----- Look if function exists - if ( (!function_exists("get_magic_quotes_runtime")) - || (!function_exists("set_magic_quotes_runtime"))) { - return $v_result; - } - - // ----- Look if already done - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Get and memorize the magic_quote value - $this->magic_quotes_status = @get_magic_quotes_runtime(); - - // ----- Disable magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime(0); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : privSwapBackMagicQuotes() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privSwapBackMagicQuotes() - { - $v_result=1; - - // ----- Look if function exists - if ( (!function_exists("get_magic_quotes_runtime")) - || (!function_exists("set_magic_quotes_runtime"))) { - return $v_result; - } - - // ----- Look if something to do - if ($this->magic_quotes_status != -1) { - return $v_result; - } - - // ----- Swap back magic_quotes - if ($this->magic_quotes_status == 1) { - @set_magic_quotes_runtime($this->magic_quotes_status); - } - - // ----- Return - return $v_result; - } - // -------------------------------------------------------------------------------- - - } - // End of class - // -------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathReduction() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilPathReduction($p_dir) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilPathReduction() +// Description : +// Parameters : +// Return Values : +// -------------------------------------------------------------------------------- +function PclZipUtilPathReduction($p_dir) +{ $v_result = ""; // ----- Look for not empty path if ($p_dir != "") { - // ----- Explode path by directory names - $v_list = explode("/", $p_dir); + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); - // ----- Study directories from last to first - $v_skip = 0; - for ($i=sizeof($v_list)-1; $i>=0; $i--) { - // ----- Look for current path - if ($v_list[$i] == ".") { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") { - $v_skip++; - } - else if ($v_list[$i] == "") { - // ----- First '/' i.e. root slash - if ($i == 0) { - $v_result = "/".$v_result; - if ($v_skip > 0) { - // ----- It is an invalid path, so the path is not modified - // TBC - $v_result = $p_dir; - $v_skip = 0; + // ----- Study directories from last to first + $v_skip = 0; + for ($i = sizeof($v_list) - 1; $i >= 0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } elseif ($v_list[$i] == "..") { + $v_skip++; + } elseif ($v_list[$i] == "") { + // ----- First '/' i.e. root slash + if ($i == 0) { + $v_result = "/" . $v_result; + if ($v_skip > 0) { + // ----- It is an invalid path, so the path is not modified + // TBC + $v_result = $p_dir; + $v_skip = 0; + } + + // ----- Last '/' i.e. indicates a directory + } elseif ($i == (sizeof($v_list) - 1)) { + $v_result = $v_list[$i]; + + // ----- Double '/' inside the path + } else { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + } else { + // ----- Look for item to skip + if ($v_skip > 0) { + $v_skip--; + } else { + $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? "/" . $v_result : ""); + } } - } - // ----- Last '/' i.e. indicates a directory - else if ($i == (sizeof($v_list)-1)) { - $v_result = $v_list[$i]; - } - // ----- Double '/' inside the path - else { - // ----- Ignore only the double '//' in path, - // but not the first and last '/' - } } - else { - // ----- Look for item to skip - if ($v_skip > 0) { - $v_skip--; - } - else { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); - } - } - } - // ----- Look for skip - if ($v_skip > 0) { - while ($v_skip > 0) { - $v_result = '../'.$v_result; - $v_skip--; + // ----- Look for skip + if ($v_skip > 0) { + while ($v_skip > 0) { + $v_result = '../' . $v_result; + $v_skip--; + } } - } } // ----- Return return $v_result; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathInclusion() - // Description : - // This function indicates if the path $p_path is under the $p_dir tree. Or, - // said in an other way, if the file or sub-dir $p_path is inside the dir - // $p_dir. - // The function indicates also if the path is exactly the same as the dir. - // This function supports path with duplicated '/' like '//', but does not - // support '.' or '..' statements. - // Parameters : - // Return Values : - // 0 if $p_path is not inside directory $p_dir - // 1 if $p_path is inside directory $p_dir - // 2 if $p_path is exactly the same as $p_dir - // -------------------------------------------------------------------------------- - function PclZipUtilPathInclusion($p_dir, $p_path) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilPathInclusion() +// Description : +// This function indicates if the path $p_path is under the $p_dir tree. Or, +// said in an other way, if the file or sub-dir $p_path is inside the dir +// $p_dir. +// The function indicates also if the path is exactly the same as the dir. +// This function supports path with duplicated '/' like '//', but does not +// support '.' or '..' statements. +// Parameters : +// Return Values : +// 0 if $p_path is not inside directory $p_dir +// 1 if $p_path is inside directory $p_dir +// 2 if $p_path is exactly the same as $p_dir +// -------------------------------------------------------------------------------- +function PclZipUtilPathInclusion($p_dir, $p_path) +{ $v_result = 1; // ----- Look for path beginning by ./ - if ( ($p_dir == '.') - || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { - $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); + if (($p_dir == '.') || ((strlen($p_dir) >= 2) && (substr($p_dir, 0, 2) == './'))) { + $p_dir = PclZipUtilTranslateWinPath(getcwd(), false) . '/' . substr($p_dir, 1); } - if ( ($p_path == '.') - || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { - $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); + if (($p_path == '.') || ((strlen($p_path) >= 2) && (substr($p_path, 0, 2) == './'))) { + $p_path = PclZipUtilTranslateWinPath(getcwd(), false) . '/' . substr($p_path, 1); } // ----- Explode dir and path by directory separator - $v_list_dir = explode("/", $p_dir); - $v_list_dir_size = sizeof($v_list_dir); - $v_list_path = explode("/", $p_path); + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); $v_list_path_size = sizeof($v_list_path); // ----- Study directories paths @@ -5499,193 +5234,182 @@ $j = 0; while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { - // ----- Look for empty dir (path reduction) - if ($v_list_dir[$i] == '') { + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ($v_list_path[$j] != '')) { + $v_result = 0; + } + + // ----- Next items $i++; - continue; - } - if ($v_list_path[$j] == '') { $j++; - continue; - } - - // ----- Compare the items - if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { - $v_result = 0; - } - - // ----- Next items - $i++; - $j++; } // ----- Look if everything seems to be the same if ($v_result) { - // ----- Skip all the empty items - while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; - while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) { + $j++; + } + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) { + $i++; + } - if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { - // ----- There are exactly the same - $v_result = 2; - } - else if ($i < $v_list_dir_size) { - // ----- The path is shorter than the dir - $v_result = 0; - } + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } elseif ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } } // ----- Return return $v_result; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilCopyBlock() - // Description : - // Parameters : - // $p_mode : read/write compression mode - // 0 : src & dest normal - // 1 : src gzip, dest normal - // 2 : src normal, dest gzip - // 3 : src & dest gzip - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilCopyBlock() +// Description : +// Parameters : +// $p_mode : read/write compression mode +// 0 : src & dest normal +// 1 : src gzip, dest normal +// 2 : src normal, dest gzip +// 3 : src & dest gzip +// Return Values : +// -------------------------------------------------------------------------------- +function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode = 0) +{ $v_result = 1; - if ($p_mode==0) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==1) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==2) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @fread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==3) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - $v_buffer = @gzread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } + if ($p_mode == 0) { + while ($p_size != 0) { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } elseif ($p_mode == 1) { + while ($p_size != 0) { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } elseif ($p_mode == 2) { + while ($p_size != 0) { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } elseif ($p_mode == 3) { + while ($p_size != 0) { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } } // ----- Return return $v_result; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilRename() - // Description : - // This function tries to do a simple rename() function. If it fails, it - // tries to copy the $p_src file in a new $p_dest file and then unlink the - // first one. - // Parameters : - // $p_src : Old filename - // $p_dest : New filename - // Return Values : - // 1 on success, 0 on failure. - // -------------------------------------------------------------------------------- - function PclZipUtilRename($p_src, $p_dest) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilRename() +// Description : +// This function tries to do a simple rename() function. If it fails, it +// tries to copy the $p_src file in a new $p_dest file and then unlink the +// first one. +// Parameters : +// $p_src : Old filename +// $p_dest : New filename +// Return Values : +// 1 on success, 0 on failure. +// -------------------------------------------------------------------------------- +function PclZipUtilRename($p_src, $p_dest) +{ $v_result = 1; // ----- Try to rename the files if (!@rename($p_src, $p_dest)) { - // ----- Try to copy & unlink the src - if (!@copy($p_src, $p_dest)) { - $v_result = 0; - } - else if (!@unlink($p_src)) { - $v_result = 0; - } + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + $v_result = 0; + } elseif (!@unlink($p_src)) { + $v_result = 0; + } } // ----- Return return $v_result; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilOptionText() - // Description : - // Translate option value in text. Mainly for debug purpose. - // Parameters : - // $p_option : the option value. - // Return Values : - // The option text value. - // -------------------------------------------------------------------------------- - function PclZipUtilOptionText($p_option) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilOptionText() +// Description : +// Translate option value in text. Mainly for debug purpose. +// Parameters : +// $p_option : the option value. +// Return Values : +// The option text value. +// -------------------------------------------------------------------------------- +function PclZipUtilOptionText($p_option) +{ $v_list = get_defined_constants(); for (reset($v_list); $v_key = key($v_list); next($v_list)) { $v_prefix = substr($v_key, 0, 10); - if (( ($v_prefix == 'PCLZIP_OPT') - || ($v_prefix == 'PCLZIP_CB_') - || ($v_prefix == 'PCLZIP_ATT')) - && ($v_list[$v_key] == $p_option)) { - return $v_key; + if ((($v_prefix == 'PCLZIP_OPT') || ($v_prefix == 'PCLZIP_CB_') || ($v_prefix == 'PCLZIP_ATT')) && ($v_list[$v_key] == $p_option)) { + return $v_key; } } $v_result = 'Unknown'; return $v_result; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilTranslateWinPath() - // Description : - // Translate windows path by replacing '\' by '/' and optionally removing - // drive letter. - // Parameters : - // $p_path : path to translate. - // $p_remove_disk_letter : true | false - // Return Values : - // The path translated. - // -------------------------------------------------------------------------------- - function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) - { +// -------------------------------------------------------------------------------- +// Function : PclZipUtilTranslateWinPath() +// Description : +// Translate windows path by replacing '\' by '/' and optionally removing +// drive letter. +// Parameters : +// $p_path : path to translate. +// $p_remove_disk_letter : true | false +// Return Values : +// The path translated. +// -------------------------------------------------------------------------------- +function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter = true) +{ if (stristr(php_uname(), 'windows')) { - // ----- Look for potential disk letter - if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position + 1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } } + return $p_path; - } - // -------------------------------------------------------------------------------- +} +// -------------------------------------------------------------------------------- diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index 4dc4af4e..2783e17e 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -37,7 +37,7 @@ use PhpOffice\PhpWord\Settings; class ZipArchive { /** @const int Flags for open method */ - const CREATE = 1; // Emulate \ZipArchive::CREATE + const CREATE = 1; // Emulate \ZipArchive::CREATE const OVERWRITE = 8; // Emulate \ZipArchive::OVERWRITE /** @@ -140,7 +140,8 @@ class ZipArchive } else { $zip = new \PclZip($this->filename); $this->tempDir = Settings::getTempDir(); - $this->numFiles = count($zip->listContent()); + $zipContent = $zip->listContent(); + $this->numFiles = is_array($zipContent) ? count($zipContent) : 0; } $this->zip = $zip; @@ -150,17 +151,17 @@ class ZipArchive /** * Close the active archive * - * @return bool - * * @throws \PhpOffice\PhpWord\Exception\Exception * + * @return bool + * * @codeCoverageIgnore Can't find any test case. Uncomment when found. */ public function close() { if (!$this->usePclzip) { if ($this->zip->close() === false) { - throw new Exception("Could not close zip file {$this->filename}."); + throw new Exception("Could not close zip file {$this->filename}: "); } } @@ -183,9 +184,9 @@ class ZipArchive if (!$this->usePclzip) { return $this->zip->extractTo($destination, $entries); - } else { - return $this->pclzipExtractTo($destination, $entries); } + + return $this->pclzipExtractTo($destination, $entries); } /** @@ -301,6 +302,7 @@ class ZipArchive // Extract all files if (is_null($entries)) { $result = $zip->extract(PCLZIP_OPT_PATH, $destination); + return ($result > 0) ? true : false; } @@ -350,7 +352,7 @@ class ZipArchive * Returns the name of an entry using its index (emulate \ZipArchive) * * @param int $index - * @return string + * @return string|bool * @since 0.10.0 */ public function pclzipGetNameIndex($index) @@ -360,9 +362,9 @@ class ZipArchive $list = $zip->listContent(); if (isset($list[$index])) { return $list[$index]['filename']; - } else { - return false; } + + return false; } /** diff --git a/src/PhpWord/SimpleType/DocProtect.php b/src/PhpWord/SimpleType/DocProtect.php new file mode 100644 index 00000000..e386913d --- /dev/null +++ b/src/PhpWord/SimpleType/DocProtect.php @@ -0,0 +1,55 @@ + array( - self::START, - self::CENTER, - self::END, - self::BOTH, - self::MEDIUM_KASHIDA, - self::DISTRIBUTE, - self::NUM_TAB, - self::HIGH_KASHIDA, - self::LOW_KASHIDA, - self::THAI_DISTRIBUTE, - self::LEFT, - self::RIGHT, - self::JUSTIFY, - ), - 'strict' => InArray::COMPARE_STRICT, - ) - ); - } } diff --git a/src/PhpWord/SimpleType/JcTable.php b/src/PhpWord/SimpleType/JcTable.php index d9648477..924a4f20 100644 --- a/src/PhpWord/SimpleType/JcTable.php +++ b/src/PhpWord/SimpleType/JcTable.php @@ -10,14 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\SimpleType; -use Zend\Validator\InArray; +use PhpOffice\PhpWord\Shared\AbstractEnum; /** * Table Alignment Type. @@ -25,28 +25,10 @@ use Zend\Validator\InArray; * Introduced in ISO/IEC-29500:2008. * * @since 0.13.0 - * - * @codeCoverageIgnore */ -final class JcTable +final class JcTable extends AbstractEnum { const START = 'start'; const CENTER = 'center'; const END = 'end'; - - /** - * @since 0.13.0 - * - * @return \Zend\Validator\InArray - */ - final public static function getValidator() - { - // todo: consider caching validator instances. - return new InArray( - array ( - 'haystack' => array(self::START, self::CENTER, self::END), - 'strict' => InArray::COMPARE_STRICT, - ) - ); - } } diff --git a/src/PhpWord/SimpleType/LineSpacingRule.php b/src/PhpWord/SimpleType/LineSpacingRule.php new file mode 100644 index 00000000..8fd8340c --- /dev/null +++ b/src/PhpWord/SimpleType/LineSpacingRule.php @@ -0,0 +1,45 @@ +$method(); - } else { - return null; } + + return null; } /** @@ -242,12 +243,12 @@ abstract class AbstractStyle protected function setIntVal($value, $default = null) { if (is_string($value) && (preg_match('/[^\d]/', $value) == 0)) { - $value = intval($value); + $value = (int) $value; } if (!is_numeric($value)) { $value = $default; } else { - $value = intval($value); + $value = (int) $value; } return $value; @@ -263,7 +264,7 @@ abstract class AbstractStyle protected function setFloatVal($value, $default = null) { if (is_string($value) && (preg_match('/[^\d\.\,]/', $value) == 0)) { - $value = floatval($value); + $value = (float) $value; } if (!is_numeric($value)) { $value = $default; @@ -279,14 +280,13 @@ abstract class AbstractStyle * @param array $enum * @param mixed $default * - * @return mixed - * * @throws \InvalidArgumentException + * @return mixed */ protected function setEnumVal($value = null, $enum = array(), $default = null) { if ($value != null && trim($value) != '' && !empty($enum) && !in_array($value, $enum)) { - throw new \InvalidArgumentException("Invalid style value: {$value} Options:".join(',', $enum)); + throw new \InvalidArgumentException("Invalid style value: {$value} Options:" . implode(',', $enum)); } elseif ($value === null || trim($value) == '') { $value = $default; } @@ -329,7 +329,7 @@ abstract class AbstractStyle protected function setPairedVal(&$property, &$pairProperty, $value) { $property = $this->setBoolVal($value, $property); - if ($value == true) { + if ($value === true) { $pairProperty = false; } diff --git a/src/PhpWord/Style/Border.php b/src/PhpWord/Style/Border.php index d3bc2e57..d032d07f 100644 --- a/src/PhpWord/Style/Border.php +++ b/src/PhpWord/Style/Border.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -36,6 +36,13 @@ class Border extends AbstractStyle */ protected $borderTopColor; + /** + * Border Top Style + * + * @var string + */ + protected $borderTopStyle; + /** * Border Left Size * @@ -50,6 +57,13 @@ class Border extends AbstractStyle */ protected $borderLeftColor; + /** + * Border Left Style + * + * @var string + */ + protected $borderLeftStyle; + /** * Border Right Size * @@ -64,6 +78,13 @@ class Border extends AbstractStyle */ protected $borderRightColor; + /** + * Border Right Style + * + * @var string + */ + protected $borderRightStyle; + /** * Border Bottom Size * @@ -78,10 +99,17 @@ class Border extends AbstractStyle */ protected $borderBottomColor; + /** + * Border Bottom Style + * + * @var string + */ + protected $borderBottomStyle; + /** * Get border size * - * @return integer[] + * @return int[] */ public function getBorderSize() { @@ -140,6 +168,37 @@ class Border extends AbstractStyle return $this; } + /** + * Get border style + * + * @return string[] + */ + public function getBorderStyle() + { + return array( + $this->getBorderTopStyle(), + $this->getBorderLeftStyle(), + $this->getBorderRightStyle(), + $this->getBorderBottomStyle(), + ); + } + + /** + * Set border style + * + * @param string $value + * @return self + */ + public function setBorderStyle($value = null) + { + $this->setBorderTopStyle($value); + $this->setBorderLeftStyle($value); + $this->setBorderRightStyle($value); + $this->setBorderBottomStyle($value); + + return $this; + } + /** * Get border top size * @@ -186,6 +245,29 @@ class Border extends AbstractStyle return $this; } + /** + * Get border top style + * + * @return string + */ + public function getBorderTopStyle() + { + return $this->borderTopStyle; + } + + /** + * Set border top Style + * + * @param string $value + * @return self + */ + public function setBorderTopStyle($value = null) + { + $this->borderTopStyle = $value; + + return $this; + } + /** * Get border left size * @@ -232,6 +314,29 @@ class Border extends AbstractStyle return $this; } + /** + * Get border left style + * + * @return string + */ + public function getBorderLeftStyle() + { + return $this->borderLeftStyle; + } + + /** + * Set border left style + * + * @param string $value + * @return self + */ + public function setBorderLeftStyle($value = null) + { + $this->borderLeftStyle = $value; + + return $this; + } + /** * Get border right size * @@ -278,6 +383,29 @@ class Border extends AbstractStyle return $this; } + /** + * Get border right style + * + * @return string + */ + public function getBorderRightStyle() + { + return $this->borderRightStyle; + } + + /** + * Set border right style + * + * @param string $value + * @return self + */ + public function setBorderRightStyle($value = null) + { + $this->borderRightStyle = $value; + + return $this; + } + /** * Get border bottom size * @@ -324,6 +452,29 @@ class Border extends AbstractStyle return $this; } + /** + * Get border bottom style + * + * @return string + */ + public function getBorderBottomStyle() + { + return $this->borderBottomStyle; + } + + /** + * Set border bottom style + * + * @param string $value + * @return self + */ + public function setBorderBottomStyle($value = null) + { + $this->borderBottomStyle = $value; + + return $this; + } + /** * Check if any of the border is not null * diff --git a/src/PhpWord/Style/Cell.php b/src/PhpWord/Style/Cell.php index 7bab8b56..e609e190 100644 --- a/src/PhpWord/Style/Cell.php +++ b/src/PhpWord/Style/Cell.php @@ -10,13 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\SimpleType\TblWidth; + /** * Table cell style */ @@ -32,13 +34,31 @@ class Cell extends Border const VALIGN_BOTTOM = 'bottom'; const VALIGN_BOTH = 'both'; + //Text direction constants /** - * Text direction constants - * - * @const string + * Left to Right, Top to Bottom + */ + const TEXT_DIR_LRTB = 'lrTb'; + /** + * Top to Bottom, Right to Left + */ + const TEXT_DIR_TBRL = 'tbRl'; + /** + * Bottom to Top, Left to Right */ const TEXT_DIR_BTLR = 'btLr'; - const TEXT_DIR_TBRL = 'tbRl'; + /** + * Left to Right, Top to Bottom Rotated + */ + const TEXT_DIR_LRTBV = 'lrTbV'; + /** + * Top to Bottom, Right to Left Rotated + */ + const TEXT_DIR_TBRLV = 'tbRlV'; + /** + * Top to Bottom, Left to Right Rotated + */ + const TEXT_DIR_TBLRV = 'tbLrV'; /** * Vertical merge (rowspan) constants @@ -72,7 +92,7 @@ class Cell extends Border /** * colspan * - * @var integer + * @var int */ private $gridSpan; @@ -93,6 +113,20 @@ class Cell extends Border */ private $shading; + /** + * Width + * + * @var int + */ + private $width; + + /** + * Width unit + * + * @var string + */ + private $unit = TblWidth::TWIP; + /** * Get vertical align. * @@ -150,9 +184,9 @@ class Cell extends Border { if ($this->shading !== null) { return $this->shading->getFill(); - } else { - return null; } + + return null; } /** @@ -169,7 +203,7 @@ class Cell extends Border /** * Get grid span (colspan). * - * @return integer + * @return int */ public function getGridSpan() { @@ -236,6 +270,51 @@ class Cell extends Border return $this; } + /** + * Get cell width + * + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set cell width + * + * @param int $value + * @return self + */ + public function setWidth($value) + { + $this->setIntVal($value); + + return $this; + } + + /** + * Get width unit + * + * @return string + */ + public function getUnit() + { + return $this->unit; + } + + /** + * Set width unit + * + * @param string $value + */ + public function setUnit($value) + { + $this->unit = $this->setEnumVal($value, array(TblWidth::AUTO, TblWidth::PERCENT, TblWidth::TWIP), TblWidth::TWIP); + + return $this; + } + /** * Get default border color * diff --git a/src/PhpWord/Style/Chart.php b/src/PhpWord/Style/Chart.php index 8e1f4b61..06b4829c 100644 --- a/src/PhpWord/Style/Chart.php +++ b/src/PhpWord/Style/Chart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,6 @@ namespace PhpOffice\PhpWord\Style; */ class Chart extends AbstractStyle { - /** * Width (in EMU) * @@ -46,6 +45,101 @@ class Chart extends AbstractStyle */ private $is3d = false; + /** + * A list of colors to use in the chart + * + * @var array + */ + private $colors = array(); + + /** + * Chart title + * + * @var string + */ + private $title = null; + + /** + * Chart legend visibility + * + * @var bool + */ + private $showLegend = false; + + /** + * A list of display options for data labels + * + * @var array + */ + private $dataLabelOptions = array( + 'showVal' => true, // value + 'showCatName' => true, // category name + 'showLegendKey' => false, //show the cart legend + 'showSerName' => false, // series name + 'showPercent' => false, + 'showLeaderLines' => false, + 'showBubbleSize' => false, + ); + + /** + * A string that tells the writer where to write chart labels or to skip + * "nextTo" - sets labels next to the axis (bar graphs on the left) (default) + * "low" - labels on the left side of the graph + * "high" - labels on the right side of the graph + * + * @var string + */ + private $categoryLabelPosition = 'nextTo'; + + /** + * A string that tells the writer where to write chart labels or to skip + * "nextTo" - sets labels next to the axis (bar graphs on the bottom) (default) + * "low" - labels are below the graph + * "high" - labels above the graph + * + * @var string + */ + private $valueLabelPosition = 'nextTo'; + + /** + * @var string + */ + private $categoryAxisTitle; + + /** + * @var string + */ + private $valueAxisTitle; + + /** + * The position for major tick marks + * Possible values are 'in', 'out', 'cross', 'none' + * + * @var string + */ + private $majorTickMarkPos = 'none'; + + /** + * Show labels for axis + * + * @var bool + */ + private $showAxisLabels = false; + + /** + * Show Gridlines for Y-Axis + * + * @var bool + */ + private $gridY = false; + + /** + * Show Gridlines for X-Axis + * + * @var bool + */ + private $gridX = false; + /** * Create a new instance * @@ -124,4 +218,275 @@ class Chart extends AbstractStyle return $this; } + + /** + * Get the list of colors to use in a chart. + * + * @return array + */ + public function getColors() + { + return $this->colors; + } + + /** + * Set the colors to use in a chart. + * + * @param array $value a list of colors to use in the chart + */ + public function setColors($value = array()) + { + $this->colors = $value; + + return $this; + } + + /** + * Get the chart title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Set the chart title + * + * @param string $value + */ + public function setTitle($value = null) + { + $this->title = $value; + + return $this; + } + + /** + * Get chart legend visibility + * + * @return bool + */ + public function isShowLegend() + { + return $this->showLegend; + } + + /** + * Set chart legend visibility + * + * @param bool $value + */ + public function setShowLegend($value = false) + { + $this->showLegend = $value; + + return $this; + } + + /* + * Show labels for axis + * + * @return bool + */ + public function showAxisLabels() + { + return $this->showAxisLabels; + } + + /** + * Set show Gridlines for Y-Axis + * + * @param bool $value + * @return self + */ + public function setShowAxisLabels($value = true) + { + $this->showAxisLabels = $this->setBoolVal($value, $this->showAxisLabels); + + return $this; + } + + /** + * get the list of options for data labels + * + * @return array + */ + public function getDataLabelOptions() + { + return $this->dataLabelOptions; + } + + /** + * Set values for data label options. + * This will only change values for options defined in $this->dataLabelOptions, and cannot create new ones. + * + * @param array $values [description] + */ + public function setDataLabelOptions($values = array()) + { + foreach (array_keys($this->dataLabelOptions) as $option) { + if (isset($values[$option])) { + $this->dataLabelOptions[$option] = $this->setBoolVal($values[$option], $this->dataLabelOptions[$option]); + } + } + } + + /* + * Show Gridlines for Y-Axis + * + * @return bool + */ + public function showGridY() + { + return $this->gridY; + } + + /** + * Set show Gridlines for Y-Axis + * + * @param bool $value + * @return self + */ + public function setShowGridY($value = true) + { + $this->gridY = $this->setBoolVal($value, $this->gridY); + + return $this; + } + + /** + * Get the categoryLabelPosition setting + * + * @return string + */ + public function getCategoryLabelPosition() + { + return $this->categoryLabelPosition; + } + + /** + * Set the categoryLabelPosition setting + * "none" - skips writing labels + * "nextTo" - sets labels next to the (bar graphs on the left) + * "low" - labels on the left side of the graph + * "high" - labels on the right side of the graph + * + * @param mixed $labelPosition + * @return self + */ + public function setCategoryLabelPosition($labelPosition) + { + $enum = array('nextTo', 'low', 'high'); + $this->categoryLabelPosition = $this->setEnumVal($labelPosition, $enum, $this->categoryLabelPosition); + + return $this; + } + + /** + * Get the valueAxisLabelPosition setting + * + * @return string + */ + public function getValueLabelPosition() + { + return $this->valueLabelPosition; + } + + /** + * Set the valueLabelPosition setting + * "none" - skips writing labels + * "nextTo" - sets labels next to the value + * "low" - sets labels are below the graph + * "high" - sets labels above the graph + * + * @param string + * @param mixed $labelPosition + */ + public function setValueLabelPosition($labelPosition) + { + $enum = array('nextTo', 'low', 'high'); + $this->valueLabelPosition = $this->setEnumVal($labelPosition, $enum, $this->valueLabelPosition); + + return $this; + } + + /** + * Get the categoryAxisTitle + * @return string + */ + public function getCategoryAxisTitle() + { + return $this->categoryAxisTitle; + } + + /** + * Set the title that appears on the category side of the chart + * @param string $axisTitle + */ + public function setCategoryAxisTitle($axisTitle) + { + $this->categoryAxisTitle = $axisTitle; + + return $this; + } + + /** + * Get the valueAxisTitle + * @return string + */ + public function getValueAxisTitle() + { + return $this->valueAxisTitle; + } + + /** + * Set the title that appears on the value side of the chart + * @param string $axisTitle + */ + public function setValueAxisTitle($axisTitle) + { + $this->valueAxisTitle = $axisTitle; + + return $this; + } + + public function getMajorTickPosition() + { + return $this->majorTickMarkPos; + } + + /** + * Set the position for major tick marks + * @param string $position + */ + public function setMajorTickPosition($position) + { + $enum = array('in', 'out', 'cross', 'none'); + $this->majorTickMarkPos = $this->setEnumVal($position, $enum, $this->majorTickMarkPos); + } + + /** + * Show Gridlines for X-Axis + * + * @return bool + */ + public function showGridX() + { + return $this->gridX; + } + + /** + * Set show Gridlines for X-Axis + * + * @param bool $value + * @return self + */ + public function setShowGridX($value = true) + { + $this->gridX = $this->setBoolVal($value, $this->gridX); + + return $this; + } } diff --git a/src/PhpWord/Style/Extrusion.php b/src/PhpWord/Style/Extrusion.php index d8c5e65f..4c860bcd 100644 --- a/src/PhpWord/Style/Extrusion.php +++ b/src/PhpWord/Style/Extrusion.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Style; /** * 3D extrusion style * - * @link http://www.schemacentral.com/sc/ooxml/t-o_CT_Extrusion.html + * @see http://www.schemacentral.com/sc/ooxml/t-o_CT_Extrusion.html * @since 0.12.0 */ class Extrusion extends AbstractStyle diff --git a/src/PhpWord/Style/Fill.php b/src/PhpWord/Style/Fill.php index cf6ffb41..360bcf3f 100644 --- a/src/PhpWord/Style/Fill.php +++ b/src/PhpWord/Style/Fill.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,9 +20,9 @@ namespace PhpOffice\PhpWord\Style; /** * Fill style * - * There are still lot of interesting things for this style that can be added, including gradient. See @link. + * There are still lot of interesting things for this style that can be added, including gradient. See @see . * - * @link http://www.schemacentral.com/sc/ooxml/t-v_CT_Fill.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_CT_Fill.html * @since 0.12.0 */ class Fill extends AbstractStyle diff --git a/src/PhpWord/Style/Font.php b/src/PhpWord/Style/Font.php index b625e3b8..e9f3c9d6 100644 --- a/src/PhpWord/Style/Font.php +++ b/src/PhpWord/Style/Font.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,8 +33,16 @@ class Font extends AbstractStyle const UNDERLINE_DASHLONG = 'dashLong'; const UNDERLINE_DASHLONGHEAVY = 'dashLongHeavy'; const UNDERLINE_DOUBLE = 'dbl'; - const UNDERLINE_DOTHASH = 'dotDash'; - const UNDERLINE_DOTHASHHEAVY = 'dotDashHeavy'; + /** + * @deprecated use UNDERLINE_DOTHASH instead, TODO remove in version 1.0 + */ + const UNDERLINE_DOTHASH = 'dotDash'; // Incorrect spelling, for backwards compatibility + /** + * @deprecated use UNDERLINE_DOTDASHHEAVY instead, TODO remove in version 1.0 + */ + const UNDERLINE_DOTHASHHEAVY = 'dotDashHeavy'; // Incorrect spelling, for backwards compatibility + const UNDERLINE_DOTDASH = 'dotDash'; + const UNDERLINE_DOTDASHHEAVY = 'dotDashHeavy'; const UNDERLINE_DOTDOTDASH = 'dotDotDash'; const UNDERLINE_DOTDOTDASHHEAVY = 'dotDotDashHeavy'; const UNDERLINE_DOTTED = 'dotted'; @@ -162,7 +170,7 @@ class Font extends AbstractStyle * Small caps * * @var bool - * @link http://www.schemacentral.com/sc/ooxml/e-w_smallCaps-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_smallCaps-1.html */ private $smallCaps = false; @@ -170,7 +178,7 @@ class Font extends AbstractStyle * All caps * * @var bool - * @link http://www.schemacentral.com/sc/ooxml/e-w_caps-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_caps-1.html */ private $allCaps = false; @@ -186,7 +194,7 @@ class Font extends AbstractStyle * * @var int * @since 0.12.0 - * @link http://www.schemacentral.com/sc/ooxml/e-w_w-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_w-1.html */ private $scale; @@ -195,7 +203,7 @@ class Font extends AbstractStyle * * @var int|float * @since 0.12.0 - * @link http://www.schemacentral.com/sc/ooxml/e-w_spacing-2.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_spacing-2.html */ private $spacing; @@ -204,7 +212,7 @@ class Font extends AbstractStyle * * @var int|float * @since 0.12.0 - * @link http://www.schemacentral.com/sc/ooxml/e-w_kern-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_kern-1.html */ private $kerning; @@ -223,16 +231,40 @@ class Font extends AbstractStyle private $shading; /** - * Right to left languages - * @var boolean + * Right to left languages + * + * @var bool */ private $rtl = false; + /** + * noProof (disables AutoCorrect) + * + * @var bool + * http://www.datypic.com/sc/ooxml/e-w_noProof-1.html + */ + private $noProof = false; + + /** + * Languages + * + * @var \PhpOffice\PhpWord\Style\Language + */ + private $lang; + + /** + * Vertically Raised or Lowered Text + * + * @var int Signed Half-Point Measurement + * @see http://www.datypic.com/sc/ooxml/e-w_position-1.html + */ + private $position; + /** * Create new font style * * @param string $type Type of font - * @param array $paragraph Paragraph styles definition + * @param array|string|\PhpOffice\PhpWord\Style\AbstractStyle $paragraph Paragraph styles definition */ public function __construct($type = 'text', $paragraph = null) { @@ -272,10 +304,12 @@ class Font extends AbstractStyle 'scale' => $this->getScale(), 'spacing' => $this->getSpacing(), 'kerning' => $this->getKerning(), + 'position' => $this->getPosition(), ), 'paragraph' => $this->getParagraph(), 'rtl' => $this->isRTL(), 'shading' => $this->getShading(), + 'lang' => $this->getLang(), ); return $styles; @@ -691,6 +725,29 @@ class Font extends AbstractStyle return $this; } + /** + * Get noProof (disables autocorrect) + * + * @return bool + */ + public function isNoProof() + { + return $this->noProof; + } + + /** + * Set noProof (disables autocorrect) + * + * @param bool $value + * @return $this + */ + public function setNoProof($value = false) + { + $this->noProof = $value; + + return $this; + } + /** * Get line height * @@ -725,7 +782,7 @@ class Font extends AbstractStyle } /** - * Set shading + * Set Paragraph * * @param mixed $value * @return self @@ -783,6 +840,32 @@ class Font extends AbstractStyle return $this; } + /** + * Get language + * + * @return \PhpOffice\PhpWord\Style\Language + */ + public function getLang() + { + return $this->lang; + } + + /** + * Set language + * + * @param mixed $value + * @return self + */ + public function setLang($value = null) + { + if (is_string($value) && $value != '') { + $value = new Language($value); + } + $this->setObjectVal($value, 'Language', $this->lang); + + return $this; + } + /** * Get bold * @@ -854,4 +937,27 @@ class Font extends AbstractStyle { return $this->getParagraph(); } + + /** + * Get position + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * Set position + * + * @param int $value + * @return self + */ + public function setPosition($value = null) + { + $this->position = $this->setIntVal($value, null); + + return $this; + } } diff --git a/src/PhpWord/Style/Frame.php b/src/PhpWord/Style/Frame.php index 97faacfb..e87b7a80 100644 --- a/src/PhpWord/Style/Frame.php +++ b/src/PhpWord/Style/Frame.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -171,6 +171,42 @@ class Frame extends AbstractStyle */ private $wrap; + /** + * Top wrap distance + * + * @var float + */ + private $wrapDistanceTop; + + /** + * Bottom wrap distance + * + * @var float + */ + private $wrapDistanceBottom; + + /** + * Left wrap distance + * + * @var float + */ + private $wrapDistanceLeft; + + /** + * Right wrap distance + * + * @var float + */ + private $wrapDistanceRight; + + /** + * Vertically raised or lowered text + * + * @var int + * @see http://www.datypic.com/sc/ooxml/e-w_position-1.html + */ + private $position; + /** * Create a new instance * @@ -200,7 +236,7 @@ class Frame extends AbstractStyle */ public function setAlignment($value) { - if (Jc::getValidator()->isValid($value)) { + if (Jc::isValid($value)) { $this->alignment = $value; } @@ -538,4 +574,119 @@ class Frame extends AbstractStyle return $this; } + + /** + * Get top distance from text wrap + * + * @return float + */ + public function getWrapDistanceTop() + { + return $this->wrapDistanceTop; + } + + /** + * Set top distance from text wrap + * + * @param int $value + * @return self + */ + public function setWrapDistanceTop($value = null) + { + $this->wrapDistanceTop = $this->setFloatVal($value, null); + + return $this; + } + + /** + * Get bottom distance from text wrap + * + * @return float + */ + public function getWrapDistanceBottom() + { + return $this->wrapDistanceBottom; + } + + /** + * Set bottom distance from text wrap + * + * @param float $value + * @return self + */ + public function setWrapDistanceBottom($value = null) + { + $this->wrapDistanceBottom = $this->setFloatVal($value, null); + + return $this; + } + + /** + * Get left distance from text wrap + * + * @return float + */ + public function getWrapDistanceLeft() + { + return $this->wrapDistanceLeft; + } + + /** + * Set left distance from text wrap + * + * @param float $value + * @return self + */ + public function setWrapDistanceLeft($value = null) + { + $this->wrapDistanceLeft = $this->setFloatVal($value, null); + + return $this; + } + + /** + * Get right distance from text wrap + * + * @return float + */ + public function getWrapDistanceRight() + { + return $this->wrapDistanceRight; + } + + /** + * Set right distance from text wrap + * + * @param float $value + * @return self + */ + public function setWrapDistanceRight($value = null) + { + $this->wrapDistanceRight = $this->setFloatVal($value, null); + + return $this; + } + + /** + * Get position + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * Set position + * + * @param int $value + * @return self + */ + public function setPosition($value = null) + { + $this->position = $this->setIntVal($value, null); + + return $this; + } } diff --git a/src/PhpWord/Style/Image.php b/src/PhpWord/Style/Image.php index f2c88b5f..70aafe12 100644 --- a/src/PhpWord/Style/Image.php +++ b/src/PhpWord/Style/Image.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Style; /** @@ -60,9 +61,9 @@ class Image extends Frame public function __construct() { parent::__construct(); - $this->setUnit('px'); + $this->setUnit(self::UNIT_PT); - // Backward compatilibity setting + // Backward compatibility setting // @todo Remove on 1.0.0 $this->setWrap(self::WRAPPING_STYLE_INLINE); $this->setHPos(self::POSITION_HORIZONTAL_LEFT); diff --git a/src/PhpWord/Style/Indentation.php b/src/PhpWord/Style/Indentation.php index 0408929b..e422395c 100644 --- a/src/PhpWord/Style/Indentation.php +++ b/src/PhpWord/Style/Indentation.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Style; /** * Paragraph indentation style * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_Ind.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_Ind.html * @since 0.10.0 */ class Indentation extends AbstractStyle diff --git a/src/PhpWord/Style/Language.php b/src/PhpWord/Style/Language.php new file mode 100644 index 00000000..412a76a7 --- /dev/null +++ b/src/PhpWord/Style/Language.php @@ -0,0 +1,235 @@ +setLatin($latin); + } + if (!empty($eastAsia)) { + $this->setEastAsia($eastAsia); + } + if (!empty($bidirectional)) { + $this->setBidirectional($bidirectional); + } + } + + /** + * Set the Latin Language + * + * @param string $latin + * The value for the latin language + * @return self + */ + public function setLatin($latin) + { + $this->latin = $this->validateLocale($latin); + + return $this; + } + + /** + * Get the Latin Language + * + * @return string|null + */ + public function getLatin() + { + return $this->latin; + } + + /** + * Set the Language ID + * + * @param int $langId + * The value for the language ID + * @return self + * @see https://technet.microsoft.com/en-us/library/cc287874(v=office.12).aspx + */ + public function setLangId($langId) + { + $this->langId = $langId; + + return $this; + } + + /** + * Get the Language ID + * + * @return int + */ + public function getLangId() + { + return $this->langId; + } + + /** + * Set the East Asian Language + * + * @param string $eastAsia + * The value for the east asian language + * @return self + */ + public function setEastAsia($eastAsia) + { + $this->eastAsia = $this->validateLocale($eastAsia); + + return $this; + } + + /** + * Get the East Asian Language + * + * @return string|null + */ + public function getEastAsia() + { + return $this->eastAsia; + } + + /** + * Set the Complex Script Language + * + * @param string $bidirectional + * The value for the complex script language + * @return self + */ + public function setBidirectional($bidirectional) + { + $this->bidirectional = $this->validateLocale($bidirectional); + + return $this; + } + + /** + * Get the Complex Script Language + * + * @return string|null + */ + public function getBidirectional() + { + return $this->bidirectional; + } + + /** + * Validates that the language passed is in the format xx-xx + * + * @param string $locale + * @return string + */ + private function validateLocale($locale) + { + if (strlen($locale) === 2) { + return strtolower($locale) . '-' . strtoupper($locale); + } + + if ($locale !== null && strstr($locale, '-') === false) { + throw new \InvalidArgumentException($locale . ' is not a valid language code'); + } + + return $locale; + } +} diff --git a/src/PhpWord/Style/Line.php b/src/PhpWord/Style/Line.php index f8cc4026..a9952eec 100644 --- a/src/PhpWord/Style/Line.php +++ b/src/PhpWord/Style/Line.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Style; /** @@ -55,7 +56,7 @@ class Line extends Image /** * flip Line * - * @var boolean + * @var bool */ private $flip = false; @@ -104,7 +105,7 @@ class Line extends Image /** * Get flip * - * @return boolean + * @return bool */ public function isFlip() { @@ -114,7 +115,7 @@ class Line extends Image /** * Set flip * - * @param boolean $value + * @param bool $value * @return self */ public function setFlip($value = false) @@ -143,7 +144,7 @@ class Line extends Image public function setConnectorType($value = null) { $enum = array( - self::CONNECTOR_TYPE_STRAIGHT + self::CONNECTOR_TYPE_STRAIGHT, ); $this->connectorType = $this->setEnumVal($value, $enum, $this->connectorType); @@ -216,7 +217,7 @@ class Line extends Image { $enum = array( self::ARROW_STYLE_BLOCK, self::ARROW_STYLE_CLASSIC, self::ARROW_STYLE_DIAMOND, - self::ARROW_STYLE_OPEN, self::ARROW_STYLE_OVAL + self::ARROW_STYLE_OPEN, self::ARROW_STYLE_OVAL, ); $this->beginArrow = $this->setEnumVal($value, $enum, $this->beginArrow); @@ -243,7 +244,7 @@ class Line extends Image { $enum = array( self::ARROW_STYLE_BLOCK, self::ARROW_STYLE_CLASSIC, self::ARROW_STYLE_DIAMOND, - self::ARROW_STYLE_OPEN, self::ARROW_STYLE_OVAL + self::ARROW_STYLE_OPEN, self::ARROW_STYLE_OVAL, ); $this->endArrow = $this->setEnumVal($value, $enum, $this->endArrow); @@ -271,7 +272,7 @@ class Line extends Image $enum = array( self::DASH_STYLE_DASH, self::DASH_STYLE_DASH_DOT, self::DASH_STYLE_LONG_DASH, self::DASH_STYLE_LONG_DASH_DOT, self::DASH_STYLE_LONG_DASH_DOT_DOT, self::DASH_STYLE_ROUND_DOT, - self::DASH_STYLE_SQUARE_DOT + self::DASH_STYLE_SQUARE_DOT, ); $this->dash = $this->setEnumVal($value, $enum, $this->dash); diff --git a/src/PhpWord/Style/LineNumbering.php b/src/PhpWord/Style/LineNumbering.php index e125f477..451252d8 100644 --- a/src/PhpWord/Style/LineNumbering.php +++ b/src/PhpWord/Style/LineNumbering.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,14 +20,14 @@ namespace PhpOffice\PhpWord\Style; /** * Line numbering style * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_LineNumber.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_LineNumber.html * @since 0.10.0 */ class LineNumbering extends AbstractStyle { /** @const string Line numbering restart setting http://www.schemacentral.com/sc/ooxml/a-w_restart-1.html */ - const LINE_NUMBERING_CONTINUOUS = 'continuous'; - const LINE_NUMBERING_NEW_PAGE = 'newPage'; + const LINE_NUMBERING_CONTINUOUS = 'continuous'; + const LINE_NUMBERING_NEW_PAGE = 'newPage'; const LINE_NUMBERING_NEW_SECTION = 'newSection'; /** @@ -55,7 +55,7 @@ class LineNumbering extends AbstractStyle * Line numbering restart setting continuous|newPage|newSection * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-w_restart-1.html + * @see http://www.schemacentral.com/sc/ooxml/a-w_restart-1.html */ private $restart; diff --git a/src/PhpWord/Style/ListItem.php b/src/PhpWord/Style/ListItem.php index 18ea0bf2..306ecff3 100644 --- a/src/PhpWord/Style/ListItem.php +++ b/src/PhpWord/Style/ListItem.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -38,7 +38,7 @@ class ListItem extends AbstractStyle /** * Legacy list type * - * @var integer + * @var int */ private $listType; @@ -53,7 +53,7 @@ class ListItem extends AbstractStyle /** * Numbering definition instance ID * - * @var integer + * @var int * @since 0.10.0 */ private $numId; @@ -75,7 +75,7 @@ class ListItem extends AbstractStyle /** * Get List Type * - * @return integer + * @return int */ public function getListType() { @@ -85,7 +85,7 @@ class ListItem extends AbstractStyle /** * Set legacy list type for version < 0.10.0 * - * @param integer $value + * @param int $value * @return self */ public function setListType($value = self::TYPE_BULLET_FILLED) @@ -93,7 +93,7 @@ class ListItem extends AbstractStyle $enum = array( self::TYPE_SQUARE_FILLED, self::TYPE_BULLET_FILLED, self::TYPE_BULLET_EMPTY, self::TYPE_NUMBER, - self::TYPE_NUMBER_NESTED, self::TYPE_ALPHANUM + self::TYPE_NUMBER_NESTED, self::TYPE_ALPHANUM, ); $this->listType = $this->setEnumVal($value, $enum, $this->listType); $this->getListTypeStyle(); @@ -132,7 +132,7 @@ class ListItem extends AbstractStyle /** * Get numbering Id * - * @return integer + * @return int */ public function getNumId() { @@ -151,6 +151,7 @@ class ListItem extends AbstractStyle $numStyle = "PHPWordList{$this->listType}"; if (Style::getStyle($numStyle) !== null) { $this->setNumStyle($numStyle); + return; } @@ -160,7 +161,7 @@ class ListItem extends AbstractStyle // Legacy level information $listTypeStyles = array( self::TYPE_SQUARE_FILLED => array( - 'type' => 'hybridMultilevel', + 'type' => 'hybridMultilevel', 'levels' => array( 0 => '1, bullet, , left, 720, 720, 360, Wingdings, default', 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', @@ -174,7 +175,7 @@ class ListItem extends AbstractStyle ), ), self::TYPE_BULLET_FILLED => array( - 'type' => 'hybridMultilevel', + 'type' => 'hybridMultilevel', 'levels' => array( 0 => '1, bullet, , left, 720, 720, 360, Symbol, default', 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', @@ -188,7 +189,7 @@ class ListItem extends AbstractStyle ), ), self::TYPE_BULLET_EMPTY => array( - 'type' => 'hybridMultilevel', + 'type' => 'hybridMultilevel', 'levels' => array( 0 => '1, bullet, o, left, 720, 720, 360, Courier New, default', 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', @@ -202,7 +203,7 @@ class ListItem extends AbstractStyle ), ), self::TYPE_NUMBER => array( - 'type' => 'hybridMultilevel', + 'type' => 'hybridMultilevel', 'levels' => array( 0 => '1, decimal, %1., left, 720, 720, 360, , default', 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', @@ -216,7 +217,7 @@ class ListItem extends AbstractStyle ), ), self::TYPE_NUMBER_NESTED => array( - 'type' => 'multilevel', + 'type' => 'multilevel', 'levels' => array( 0 => '1, decimal, %1., left, 360, 360, 360, , ', 1 => '1, decimal, %1.%2., left, 792, 792, 432, , ', @@ -230,7 +231,7 @@ class ListItem extends AbstractStyle ), ), self::TYPE_ALPHANUM => array( - 'type' => 'multilevel', + 'type' => 'multilevel', 'levels' => array( 0 => '1, decimal, %1., left, 720, 720, 360, , ', 1 => '1, lowerLetter, %2., left, 1440, 1440, 360, , ', @@ -247,11 +248,12 @@ class ListItem extends AbstractStyle // Populate style and register to global Style register $style = $listTypeStyles[$this->listType]; + $numProperties = count($properties); foreach ($style['levels'] as $key => $value) { $level = array(); $levelProperties = explode(', ', $value); $level['level'] = $key; - for ($i = 0; $i < count($properties); $i++) { + for ($i = 0; $i < $numProperties; $i++) { $property = $properties[$i]; $level[$property] = $levelProperties[$i]; } diff --git a/src/PhpWord/Style/Numbering.php b/src/PhpWord/Style/Numbering.php index 0d4fd85d..f7855cfa 100644 --- a/src/PhpWord/Style/Numbering.php +++ b/src/PhpWord/Style/Numbering.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,9 +20,9 @@ namespace PhpOffice\PhpWord\Style; /** * Numbering style * - * @link http://www.schemacentral.com/sc/ooxml/e-w_numbering.html - * @link http://www.schemacentral.com/sc/ooxml/e-w_abstractNum-1.html - * @link http://www.schemacentral.com/sc/ooxml/e-w_num-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_numbering.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_abstractNum-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_num-1.html * @since 0.10.0 */ class Numbering extends AbstractStyle @@ -31,7 +31,7 @@ class Numbering extends AbstractStyle * Numbering definition instance ID * * @var int - * @link http://www.schemacentral.com/sc/ooxml/e-w_num-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_num-1.html */ private $numId; @@ -39,7 +39,7 @@ class Numbering extends AbstractStyle * Multilevel type singleLevel|multilevel|hybridMultilevel * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-w_val-67.html + * @see http://www.schemacentral.com/sc/ooxml/a-w_val-67.html */ private $type; @@ -53,7 +53,7 @@ class Numbering extends AbstractStyle /** * Get Id * - * @return integer + * @return int */ public function getNumId() { @@ -63,7 +63,7 @@ class Numbering extends AbstractStyle /** * Set Id * - * @param integer $value + * @param int $value * @return self */ public function setNumId($value) diff --git a/src/PhpWord/Style/NumberingLevel.php b/src/PhpWord/Style/NumberingLevel.php index 51ae6148..e9b32f01 100644 --- a/src/PhpWord/Style/NumberingLevel.php +++ b/src/PhpWord/Style/NumberingLevel.php @@ -10,19 +10,20 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\NumberFormat; /** * Numbering level definition * - * @link http://www.schemacentral.com/sc/ooxml/e-w_lvl-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_lvl-1.html * @since 0.10.0 */ class NumberingLevel extends AbstractStyle @@ -30,31 +31,31 @@ class NumberingLevel extends AbstractStyle /** * Level number, 0 to 8 (total 9 levels) * - * @var integer + * @var int */ private $level = 0; /** * Starting value w:start * - * @var integer - * @link http://www.schemacentral.com/sc/ooxml/e-w_start-1.html + * @var int + * @see http://www.schemacentral.com/sc/ooxml/e-w_start-1.html */ private $start = 1; /** - * Numbering format bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter + * Numbering format w:numFmt, one of PhpOffice\PhpWord\SimpleType\NumberFormat * * @var string - * @link http://www.schemacentral.com/sc/ooxml/t-w_ST_NumberFormat.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_ST_NumberFormat.html */ private $format; /** * Restart numbering level symbol w:lvlRestart * - * @var integer - * @link http://www.schemacentral.com/sc/ooxml/e-w_lvlRestart-1.html + * @var int + * @see http://www.schemacentral.com/sc/ooxml/e-w_lvlRestart-1.html */ private $restart; @@ -62,15 +63,15 @@ class NumberingLevel extends AbstractStyle * Related paragraph style * * @var string - * @link http://www.schemacentral.com/sc/ooxml/e-w_pStyle-2.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_pStyle-2.html */ private $pStyle; /** - * Content between numbering symbol and paragraph text + * Content between numbering symbol and paragraph text w:suff * * @var string tab|space|nothing - * @link http://www.schemacentral.com/sc/ooxml/e-w_suff-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_suff-1.html */ private $suffix = 'tab'; @@ -78,33 +79,35 @@ class NumberingLevel extends AbstractStyle * Numbering level text e.g. %1 for nonbullet or bullet character * * @var string - * @link http://www.schemacentral.com/sc/ooxml/e-w_lvlText-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_lvlText-1.html */ private $text; /** - * @var string + * Justification, w:lvlJc + * + * @var string, one of PhpOffice\PhpWord\SimpleType\Jc */ private $alignment = ''; /** * Left * - * @var integer + * @var int */ private $left; /** * Hanging * - * @var integer + * @var int */ private $hanging; /** * Tab position * - * @var integer + * @var int */ private $tabPos; @@ -119,14 +122,14 @@ class NumberingLevel extends AbstractStyle * Hint default|eastAsia|cs * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-w_hint-1.html + * @see http://www.schemacentral.com/sc/ooxml/a-w_hint-1.html */ private $hint; /** * Get level * - * @return integer + * @return int */ public function getLevel() { @@ -136,19 +139,20 @@ class NumberingLevel extends AbstractStyle /** * Set level * - * @param integer $value + * @param int $value * @return self */ public function setLevel($value) { $this->level = $this->setIntVal($value, $this->level); + return $this; } /** * Get start * - * @return integer + * @return int */ public function getStart() { @@ -158,12 +162,13 @@ class NumberingLevel extends AbstractStyle /** * Set start * - * @param integer $value + * @param int $value * @return self */ public function setStart($value) { $this->start = $this->setIntVal($value, $this->start); + return $this; } @@ -185,15 +190,15 @@ class NumberingLevel extends AbstractStyle */ public function setFormat($value) { - $enum = array('bullet', 'decimal', 'upperRoman', 'lowerRoman', 'upperLetter', 'lowerLetter'); - $this->format = $this->setEnumVal($value, $enum, $this->format); + $this->format = $this->setEnumVal($value, NumberFormat::values(), $this->format); + return $this; } /** - * Get start + * Get restart * - * @return integer + * @return int */ public function getRestart() { @@ -201,14 +206,15 @@ class NumberingLevel extends AbstractStyle } /** - * Set start + * Set restart * - * @param integer $value + * @param int $value * @return self */ public function setRestart($value) { $this->restart = $this->setIntVal($value, $this->restart); + return $this; } @@ -231,6 +237,7 @@ class NumberingLevel extends AbstractStyle public function setPStyle($value) { $this->pStyle = $value; + return $this; } @@ -254,6 +261,7 @@ class NumberingLevel extends AbstractStyle { $enum = array('tab', 'space', 'nothing'); $this->suffix = $this->setEnumVal($value, $enum, $this->suffix); + return $this; } @@ -276,6 +284,7 @@ class NumberingLevel extends AbstractStyle public function setText($value) { $this->text = $value; + return $this; } @@ -298,7 +307,7 @@ class NumberingLevel extends AbstractStyle */ public function setAlignment($value) { - if (Jc::getValidator()->isValid($value)) { + if (Jc::isValid($value)) { $this->alignment = $value; } @@ -334,7 +343,7 @@ class NumberingLevel extends AbstractStyle /** * Get left * - * @return integer + * @return int */ public function getLeft() { @@ -344,19 +353,20 @@ class NumberingLevel extends AbstractStyle /** * Set left * - * @param integer $value + * @param int $value * @return self */ public function setLeft($value) { $this->left = $this->setIntVal($value, $this->left); + return $this; } /** * Get hanging * - * @return integer + * @return int */ public function getHanging() { @@ -366,19 +376,20 @@ class NumberingLevel extends AbstractStyle /** * Set hanging * - * @param integer $value + * @param int $value * @return self */ public function setHanging($value) { $this->hanging = $this->setIntVal($value, $this->hanging); + return $this; } /** * Get tab * - * @return integer + * @return int */ public function getTabPos() { @@ -388,12 +399,13 @@ class NumberingLevel extends AbstractStyle /** * Set tab * - * @param integer $value + * @param int $value * @return self */ public function setTabPos($value) { $this->tabPos = $this->setIntVal($value, $this->tabPos); + return $this; } @@ -416,6 +428,7 @@ class NumberingLevel extends AbstractStyle public function setFont($value) { $this->font = $value; + return $this; } diff --git a/src/PhpWord/Style/Outline.php b/src/PhpWord/Style/Outline.php index 8628c4c5..a04ad974 100644 --- a/src/PhpWord/Style/Outline.php +++ b/src/PhpWord/Style/Outline.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,8 +20,8 @@ namespace PhpOffice\PhpWord\Style; /** * Outline defines the line/border of the object * - * @link http://www.schemacentral.com/sc/ooxml/t-v_CT_Stroke.html - * @link http://www.w3.org/TR/1998/NOTE-VML-19980513#_Toc416858395 + * @see http://www.schemacentral.com/sc/ooxml/t-v_CT_Stroke.html + * @see http://www.w3.org/TR/1998/NOTE-VML-19980513#_Toc416858395 * @since 0.12.0 */ class Outline extends AbstractStyle @@ -29,7 +29,7 @@ class Outline extends AbstractStyle /** * Line style constants * - * @link http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeLineStyle.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeLineStyle.html * @const string */ const LINE_SINGLE = 'single'; @@ -41,7 +41,7 @@ class Outline extends AbstractStyle /** * Line style constants * - * @link http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeEndCap.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeEndCap.html * @const string */ const ENDCAP_FLAT = 'flat'; @@ -51,7 +51,7 @@ class Outline extends AbstractStyle /** * Arrowhead type constants * - * @link http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeArrowType.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeArrowType.html * @const string */ const ARROW_NONE = 'none'; @@ -100,7 +100,7 @@ class Outline extends AbstractStyle * End cap * * @var string - * @link http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeEndCap.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_ST_StrokeEndCap.html */ private $endCap; @@ -226,7 +226,7 @@ class Outline extends AbstractStyle public function setLine($value = null) { $enum = array(self::LINE_SINGLE, self::LINE_THIN_THIN, self::LINE_THIN_THICK, - self::LINE_THICK_THIN, self::LINE_THICK_BETWEEN_THIN); + self::LINE_THICK_THIN, self::LINE_THICK_BETWEEN_THIN, ); $this->line = $this->setEnumVal($value, $enum, null); return $this; @@ -275,7 +275,7 @@ class Outline extends AbstractStyle public function setStartArrow($value = null) { $enum = array(self::ARROW_NONE, self::ARROW_BLOCK, self::ARROW_CLASSIC, - self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN); + self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN, ); $this->startArrow = $this->setEnumVal($value, $enum, null); return $this; @@ -300,7 +300,7 @@ class Outline extends AbstractStyle public function setEndArrow($value = null) { $enum = array(self::ARROW_NONE, self::ARROW_BLOCK, self::ARROW_CLASSIC, - self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN); + self::ARROW_OVAL, self::ARROW_DIAMOND, self::ARROW_OPEN, ); $this->endArrow = $this->setEnumVal($value, $enum, null); return $this; diff --git a/src/PhpWord/Style/Paper.php b/src/PhpWord/Style/Paper.php index ed1c59eb..3c93ed8f 100644 --- a/src/PhpWord/Style/Paper.php +++ b/src/PhpWord/Style/Paper.php @@ -10,13 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Shared\Converter; + /** * Paper size from ISO/IEC 29500-1:2012 pg. 1656-1657 * @@ -100,6 +102,7 @@ class Paper extends AbstractStyle 'A3' => array(297, 420, 'mm'), 'A4' => array(210, 297, 'mm'), 'A5' => array(148, 210, 'mm'), + 'B5' => array(176, 250, 'mm'), 'Folio' => array(8.5, 13, 'in'), 'Legal' => array(8.5, 14, 'in'), 'Letter' => array(8.5, 11, 'in'), @@ -115,14 +118,14 @@ class Paper extends AbstractStyle /** * Width * - * @var int (twip) + * @var float (twip) */ private $width; /** * Height * - * @var int (twip) + * @var float (twip) */ private $height; @@ -157,11 +160,14 @@ class Paper extends AbstractStyle $this->size = $this->setEnumVal($size, array_keys($this->sizes), $this->size); list($width, $height, $unit) = $this->sizes[$this->size]; - $multipliers = array('mm' => 56.5217, 'in' => 1440); - $multiplier = $multipliers[$unit]; - $this->width = (int)round($width * $multiplier); - $this->height = (int)round($height * $multiplier); + if ($unit == 'mm') { + $this->width = Converter::cmToTwip($width / 10); + $this->height = Converter::cmToTwip($height / 10); + } else { + $this->width = Converter::inchToTwip($width); + $this->height = Converter::inchToTwip($height); + } return $this; } @@ -169,7 +175,7 @@ class Paper extends AbstractStyle /** * Get width * - * @return int + * @return float */ public function getWidth() { @@ -179,7 +185,7 @@ class Paper extends AbstractStyle /** * Get height * - * @return int + * @return float */ public function getHeight() { diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index c6e60efb..ac587686 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,6 +20,7 @@ namespace PhpOffice\PhpWord\Style; use PhpOffice\Common\Text; use PhpOffice\PhpWord\Exception\InvalidStyleException; use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\TextAlignment; /** * Paragraph style @@ -46,7 +47,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * - Borders * - Background * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_PPr.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_PPr.html */ class Paragraph extends Border { @@ -158,6 +159,34 @@ class Paragraph extends Border */ private $shading; + /** + * Ignore Spacing Above and Below When Using Identical Styles + * + * @var bool + */ + private $contextualSpacing = false; + + /** + * Right to Left Paragraph Layout + * + * @var bool + */ + private $bidi = false; + + /** + * Vertical Character Alignment on Line + * + * @var string + */ + private $textAlignment; + + /** + * Suppress hyphenation for paragraph + * + * @var bool + */ + private $suppressAutoHyphens = false; + /** * Set Style value * @@ -190,24 +219,28 @@ class Paragraph extends Border public function getStyleValues() { $styles = array( - 'name' => $this->getStyleName(), - 'basedOn' => $this->getBasedOn(), - 'next' => $this->getNext(), - 'alignment' => $this->getAlignment(), - 'indentation' => $this->getIndentation(), - 'spacing' => $this->getSpace(), - 'pagination' => array( - 'widowControl' => $this->hasWidowControl(), - 'keepNext' => $this->isKeepNext(), - 'keepLines' => $this->isKeepLines(), - 'pageBreak' => $this->hasPageBreakBefore(), + 'name' => $this->getStyleName(), + 'basedOn' => $this->getBasedOn(), + 'next' => $this->getNext(), + 'alignment' => $this->getAlignment(), + 'indentation' => $this->getIndentation(), + 'spacing' => $this->getSpace(), + 'pagination' => array( + 'widowControl' => $this->hasWidowControl(), + 'keepNext' => $this->isKeepNext(), + 'keepLines' => $this->isKeepLines(), + 'pageBreak' => $this->hasPageBreakBefore(), ), - 'numbering' => array( - 'style' => $this->getNumStyle(), - 'level' => $this->getNumLevel(), + 'numbering' => array( + 'style' => $this->getNumStyle(), + 'level' => $this->getNumLevel(), ), - 'tabs' => $this->getTabs(), - 'shading' => $this->getShading(), + 'tabs' => $this->getTabs(), + 'shading' => $this->getShading(), + 'contextualSpacing' => $this->hasContextualSpacing(), + 'bidi' => $this->isBidi(), + 'textAlignment' => $this->getTextAlignment(), + 'suppressAutoHyphens' => $this->hasSuppressAutoHyphens(), ); return $styles; @@ -232,7 +265,7 @@ class Paragraph extends Border */ public function setAlignment($value) { - if (Jc::getValidator()->isValid($value)) { + if (Jc::isValid($value)) { $this->alignment = $value; } @@ -404,7 +437,7 @@ class Paragraph extends Border /** * Get space before paragraph * - * @return integer + * @return int */ public function getSpaceBefore() { @@ -425,7 +458,7 @@ class Paragraph extends Border /** * Get space after paragraph * - * @return integer + * @return int */ public function getSpaceAfter() { @@ -464,6 +497,27 @@ class Paragraph extends Border return $this->setSpace(array('line' => $value)); } + /** + * Get spacing line rule + * + * @return string + */ + public function getSpacingLineRule() + { + return $this->getChildStyleValue($this->spacing, 'lineRule'); + } + + /** + * Set the spacing line rule + * + * @param string $value Possible values are defined in LineSpacingRule + * @return \PhpOffice\PhpWord\Style\Paragraph + */ + public function setSpacingLineRule($value) + { + return $this->setSpace(array('lineRule' => $value)); + } + /** * Get line height * @@ -479,22 +533,22 @@ class Paragraph extends Border * * @param int|float|string $lineHeight * - * @return self - * * @throws \PhpOffice\PhpWord\Exception\InvalidStyleException + * @return self */ public function setLineHeight($lineHeight) { if (is_string($lineHeight)) { - $lineHeight = floatval(preg_replace('/[^0-9\.\,]/', '', $lineHeight)); + $lineHeight = (float) (preg_replace('/[^0-9\.\,]/', '', $lineHeight)); } - if ((!is_integer($lineHeight) && !is_float($lineHeight)) || !$lineHeight) { + if ((!is_int($lineHeight) && !is_float($lineHeight)) || !$lineHeight) { throw new InvalidStyleException('Line height must be a valid number'); } $this->lineHeight = $lineHeight; $this->setSpacing($lineHeight * self::LINE_HEIGHT); + return $this; } @@ -731,4 +785,91 @@ class Paragraph extends Border return $this; } + + /** + * Get contextualSpacing + * + * @return bool + */ + public function hasContextualSpacing() + { + return $this->contextualSpacing; + } + + /** + * Set contextualSpacing + * + * @param bool $contextualSpacing + * @return self + */ + public function setContextualSpacing($contextualSpacing) + { + $this->contextualSpacing = $contextualSpacing; + + return $this; + } + + /** + * Get bidirectional + * + * @return bool + */ + public function isBidi() + { + return $this->bidi; + } + + /** + * Set bidi + * + * @param bool $bidi + * Set to true to write from right to left + * @return self + */ + public function setBidi($bidi) + { + $this->bidi = $bidi; + + return $this; + } + + /** + * Get textAlignment + * + * @return string + */ + public function getTextAlignment() + { + return $this->textAlignment; + } + + /** + * Set textAlignment + * + * @param string $textAlignment + * @return self + */ + public function setTextAlignment($textAlignment) + { + TextAlignment::validate($textAlignment); + $this->textAlignment = $textAlignment; + + return $this; + } + + /** + * @return bool + */ + public function hasSuppressAutoHyphens() + { + return $this->suppressAutoHyphens; + } + + /** + * @param bool $suppressAutoHyphens + */ + public function setSuppressAutoHyphens($suppressAutoHyphens) + { + $this->suppressAutoHyphens = (bool) $suppressAutoHyphens; + } } diff --git a/src/PhpWord/Style/Row.php b/src/PhpWord/Style/Row.php index 5be03b69..ad801af6 100644 --- a/src/PhpWord/Style/Row.php +++ b/src/PhpWord/Style/Row.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Style/Section.php b/src/PhpWord/Style/Section.php index 62eb8f11..162e08e0 100644 --- a/src/PhpWord/Style/Section.php +++ b/src/PhpWord/Style/Section.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -35,20 +35,20 @@ class Section extends Border * * @const int|float */ - const DEFAULT_WIDTH = 11870; // In twips. - const DEFAULT_HEIGHT = 16787; // In twips. - const DEFAULT_MARGIN = 1440; // In twips. - const DEFAULT_GUTTER = 0; // In twips. - const DEFAULT_HEADER_HEIGHT = 720; // In twips. - const DEFAULT_FOOTER_HEIGHT = 720; // In twips. + const DEFAULT_WIDTH = 11905.511811024; // In twips. + const DEFAULT_HEIGHT = 16837.79527559; // In twips. + const DEFAULT_MARGIN = 1440; // In twips. + const DEFAULT_GUTTER = 0; // In twips. + const DEFAULT_HEADER_HEIGHT = 720; // In twips. + const DEFAULT_FOOTER_HEIGHT = 720; // In twips. const DEFAULT_COLUMN_COUNT = 1; - const DEFAULT_COLUMN_SPACING = 720; // In twips. + const DEFAULT_COLUMN_SPACING = 720; // In twips. /** * Page Orientation * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-w_orient-1.html + * @see http://www.schemacentral.com/sc/ooxml/a-w_orient-1.html */ private $orientation = self::ORIENTATION_PORTRAIT; @@ -105,7 +105,7 @@ class Section extends Border * Page gutter spacing * * @var int|float - * @link http://www.schemacentral.com/sc/ooxml/e-w_pgMar-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_pgMar-1.html */ private $gutter = self::DEFAULT_GUTTER; @@ -162,7 +162,7 @@ class Section extends Border * Line numbering * * @var \PhpOffice\PhpWord\Style\LineNumbering - * @link http://www.schemacentral.com/sc/ooxml/e-w_lnNumType-1.html + * @see http://www.schemacentral.com/sc/ooxml/e-w_lnNumType-1.html */ private $lineNumbering; @@ -504,6 +504,7 @@ class Section extends Border public function setPageNumberingStart($pageNumberingStart = null) { $this->pageNumberingStart = $pageNumberingStart; + return $this; } @@ -572,6 +573,7 @@ class Section extends Border public function setBreakType($value = null) { $this->breakType = $value; + return $this; } diff --git a/src/PhpWord/Style/Shading.php b/src/PhpWord/Style/Shading.php index ab4fce82..154df26c 100644 --- a/src/PhpWord/Style/Shading.php +++ b/src/PhpWord/Style/Shading.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Style; /** * Shading style * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_Shd.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_Shd.html * @since 0.10.0 */ class Shading extends AbstractStyle @@ -29,7 +29,7 @@ class Shading extends AbstractStyle * Pattern constants (partly) * * @const string - * @link http://www.schemacentral.com/sc/ooxml/t-w_ST_Shd.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_ST_Shd.html */ const PATTERN_CLEAR = 'clear'; // No pattern const PATTERN_SOLID = 'solid'; // 100% fill pattern @@ -43,7 +43,7 @@ class Shading extends AbstractStyle * Shading pattern * * @var string - * @link http://www.schemacentral.com/sc/ooxml/t-w_ST_Shd.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_ST_Shd.html */ private $pattern = self::PATTERN_CLEAR; @@ -91,7 +91,7 @@ class Shading extends AbstractStyle { $enum = array( self::PATTERN_CLEAR, self::PATTERN_SOLID, self::PATTERN_HSTRIPE, - self::PATTERN_VSTRIPE, self::PATTERN_DSTRIPE, self::PATTERN_HCROSS, self::PATTERN_DCROSS + self::PATTERN_VSTRIPE, self::PATTERN_DSTRIPE, self::PATTERN_HCROSS, self::PATTERN_DCROSS, ); $this->pattern = $this->setEnumVal($value, $enum, $this->pattern); diff --git a/src/PhpWord/Style/Shadow.php b/src/PhpWord/Style/Shadow.php index f8f693a9..1379a320 100644 --- a/src/PhpWord/Style/Shadow.php +++ b/src/PhpWord/Style/Shadow.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Style; /** * Shadow style * - * @link http://www.schemacentral.com/sc/ooxml/t-v_CT_Shadow.html + * @see http://www.schemacentral.com/sc/ooxml/t-v_CT_Shadow.html * @since 0.12.0 */ class Shadow extends AbstractStyle diff --git a/src/PhpWord/Style/Shape.php b/src/PhpWord/Style/Shape.php index 01b61588..0c3f8179 100644 --- a/src/PhpWord/Style/Shape.php +++ b/src/PhpWord/Style/Shape.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Style/Spacing.php b/src/PhpWord/Style/Spacing.php index 8d7cfeb2..9bfb2282 100644 --- a/src/PhpWord/Style/Spacing.php +++ b/src/PhpWord/Style/Spacing.php @@ -10,17 +10,19 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\SimpleType\LineSpacingRule; + /** * Spacing between lines and above/below paragraph style * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_Spacing.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_Spacing.html * @since 0.10.0 */ class Spacing extends AbstractStyle @@ -51,7 +53,7 @@ class Spacing extends AbstractStyle * * @var string */ - private $rule = 'auto'; + private $lineRule = LineSpacingRule::AUTO; /** * Create a new instance @@ -137,9 +139,9 @@ class Spacing extends AbstractStyle * * @return string */ - public function getRule() + public function getLineRule() { - return $this->rule; + return $this->lineRule; } /** @@ -148,9 +150,37 @@ class Spacing extends AbstractStyle * @param string $value * @return self */ + public function setLineRule($value = null) + { + LineSpacingRule::validate($value); + $this->lineRule = $value; + + return $this; + } + + /** + * Get line rule + * + * @return string + * @deprecated Use getLineRule() instead + * @codeCoverageIgnore + */ + public function getRule() + { + return $this->lineRule; + } + + /** + * Set line rule + * + * @param string $value + * @return self + * @deprecated Use setLineRule() instead + * @codeCoverageIgnore + */ public function setRule($value = null) { - $this->rule = $value; + $this->lineRule = $value; return $this; } diff --git a/src/PhpWord/Style/TOC.php b/src/PhpWord/Style/TOC.php index eb4b2253..2efd54a4 100644 --- a/src/PhpWord/Style/TOC.php +++ b/src/PhpWord/Style/TOC.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Style/Tab.php b/src/PhpWord/Style/Tab.php index 33e518c8..d3cf5bd7 100644 --- a/src/PhpWord/Style/Tab.php +++ b/src/PhpWord/Style/Tab.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -27,25 +27,25 @@ class Tab extends AbstractStyle * * @const string */ - const TAB_STOP_CLEAR = 'clear'; - const TAB_STOP_LEFT = 'left'; - const TAB_STOP_CENTER = 'center'; - const TAB_STOP_RIGHT = 'right'; + const TAB_STOP_CLEAR = 'clear'; + const TAB_STOP_LEFT = 'left'; + const TAB_STOP_CENTER = 'center'; + const TAB_STOP_RIGHT = 'right'; const TAB_STOP_DECIMAL = 'decimal'; - const TAB_STOP_BAR = 'bar'; - const TAB_STOP_NUM = 'num'; + const TAB_STOP_BAR = 'bar'; + const TAB_STOP_NUM = 'num'; /** * Tab leader types * * @const string */ - const TAB_LEADER_NONE = 'none'; - const TAB_LEADER_DOT = 'dot'; - const TAB_LEADER_HYPHEN = 'hyphen'; + const TAB_LEADER_NONE = 'none'; + const TAB_LEADER_DOT = 'dot'; + const TAB_LEADER_HYPHEN = 'hyphen'; const TAB_LEADER_UNDERSCORE = 'underscore'; - const TAB_LEADER_HEAVY = 'heavy'; - const TAB_LEADER_MIDDLEDOT = 'middleDot'; + const TAB_LEADER_HEAVY = 'heavy'; + const TAB_LEADER_MIDDLEDOT = 'middleDot'; /** * Tab stop type @@ -73,19 +73,19 @@ class Tab extends AbstractStyle * must conform to the values put forth in the schema. If they do not * they will be changed to default values. * - * @param string $type Defaults to 'clear' if value is not possible. - * @param int $position Must be numeric; otherwise defaults to 0. - * @param string $leader Defaults to null if value is not possible. + * @param string $type Defaults to 'clear' if value is not possible + * @param int $position Must be numeric; otherwise defaults to 0 + * @param string $leader Defaults to null if value is not possible */ public function __construct($type = null, $position = 0, $leader = null) { $stopTypes = array( - self::TAB_STOP_CLEAR, self::TAB_STOP_LEFT,self::TAB_STOP_CENTER, - self::TAB_STOP_RIGHT, self::TAB_STOP_DECIMAL, self::TAB_STOP_BAR, self::TAB_STOP_NUM + self::TAB_STOP_CLEAR, self::TAB_STOP_LEFT, self::TAB_STOP_CENTER, + self::TAB_STOP_RIGHT, self::TAB_STOP_DECIMAL, self::TAB_STOP_BAR, self::TAB_STOP_NUM, ); $leaderTypes = array( self::TAB_LEADER_NONE, self::TAB_LEADER_DOT, self::TAB_LEADER_HYPHEN, - self::TAB_LEADER_UNDERSCORE, self::TAB_LEADER_HEAVY, self::TAB_LEADER_MIDDLEDOT + self::TAB_LEADER_UNDERSCORE, self::TAB_LEADER_HEAVY, self::TAB_LEADER_MIDDLEDOT, ); $this->type = $this->setEnumVal($type, $stopTypes, $this->type); diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php index 91809528..0f7bf7dc 100644 --- a/src/PhpWord/Style/Table.php +++ b/src/PhpWord/Style/Table.php @@ -10,25 +10,47 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType; use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\JcTable; +use PhpOffice\PhpWord\SimpleType\TblWidth; class Table extends Border { /** - * @const string Table width units http://www.schemacentral.com/sc/ooxml/t-w_ST_TblWidth.html + * @deprecated Use \PhpOffice\PhpWord\SimpleType\TblWidth::AUTO instead */ const WIDTH_AUTO = 'auto'; // Automatically determined width + /** + * @deprecated Use \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT instead + */ const WIDTH_PERCENT = 'pct'; // Width in fiftieths (1/50) of a percent (1% = 50 unit) + /** + * @deprecated Use \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP instead + */ const WIDTH_TWIP = 'dxa'; // Width in twentieths (1/20) of a point (twip) + //values for http://www.datypic.com/sc/ooxml/t-w_ST_TblLayoutType.html + /** + * AutoFit Table Layout + * + * @var string + */ + const LAYOUT_AUTO = 'autofit'; + /** + * Fixed Width Table Layout + * + * @var string + */ + const LAYOUT_FIXED = 'fixed'; + /** * Is this a first row style? * @@ -119,7 +141,34 @@ class Table extends Border /** * @var string Width unit */ - private $unit = self::WIDTH_AUTO; + private $unit = TblWidth::AUTO; + + /** + * @var int|float cell spacing value + */ + protected $cellSpacing = null; + + /** + * @var string Table Layout + */ + private $layout = self::LAYOUT_AUTO; + + /** + * Position + * + * @var \PhpOffice\PhpWord\Style\TablePosition + */ + private $position; + + /** @var TblWidthComplexType|null */ + private $indent; + + /** + * The width of each column, computed based on the max cell width of each column + * + * @var int[] + */ + private $columnWidths; /** * Create new table style @@ -133,15 +182,7 @@ class Table extends Border if ($firstRowStyle !== null && is_array($firstRowStyle)) { $this->firstRowStyle = clone $this; $this->firstRowStyle->isFirstRow = true; - unset($this->firstRowStyle->firstRowStyle); - unset($this->firstRowStyle->borderInsideHSize); - unset($this->firstRowStyle->borderInsideHColor); - unset($this->firstRowStyle->borderInsideVSize); - unset($this->firstRowStyle->borderInsideVColor); - unset($this->firstRowStyle->cellMarginTop); - unset($this->firstRowStyle->cellMarginLeft); - unset($this->firstRowStyle->cellMarginRight); - unset($this->firstRowStyle->cellMarginBottom); + unset($this->firstRowStyle->firstRowStyle, $this->firstRowStyle->borderInsideHSize, $this->firstRowStyle->borderInsideHColor, $this->firstRowStyle->borderInsideVSize, $this->firstRowStyle->borderInsideVColor, $this->firstRowStyle->cellMarginTop, $this->firstRowStyle->cellMarginLeft, $this->firstRowStyle->cellMarginRight, $this->firstRowStyle->cellMarginBottom, $this->firstRowStyle->cellSpacing); $this->firstRowStyle->setStyleByArray($firstRowStyle); } @@ -150,6 +191,22 @@ class Table extends Border } } + /** + * @param float|int $cellSpacing + */ + public function setCellSpacing($cellSpacing = null) + { + $this->cellSpacing = $cellSpacing; + } + + /** + * @return float|int + */ + public function getCellSpacing() + { + return $this->cellSpacing; + } + /** * Set first row * @@ -190,7 +247,7 @@ class Table extends Border /** * Get TLRBHV Border Size * - * @return integer[] + * @return int[] */ public function getBorderSize() { @@ -428,7 +485,7 @@ class Table extends Border /** * Get cell margin * - * @return integer[] + * @return int[] */ public function getCellMargin() { @@ -436,7 +493,7 @@ class Table extends Border $this->cellMarginTop, $this->cellMarginLeft, $this->cellMarginRight, - $this->cellMarginBottom + $this->cellMarginBottom, ); } @@ -510,7 +567,7 @@ class Table extends Border */ public function setAlignment($value) { - if (JcTable::getValidator()->isValid($value) || Jc::getValidator()->isValid($value)) { + if (JcTable::isValid($value) || Jc::isValid($value)) { $this->alignment = $value; } @@ -584,8 +641,32 @@ class Table extends Border */ public function setUnit($value = null) { - $enum = array(self::WIDTH_AUTO, self::WIDTH_PERCENT, self::WIDTH_TWIP); - $this->unit = $this->setEnumVal($value, $enum, $this->unit); + TblWidth::validate($value); + $this->unit = $value; + + return $this; + } + + /** + * Get layout + * + * @return string + */ + public function getLayout() + { + return $this->layout; + } + + /** + * Set layout + * + * @param string $value + * @return self + */ + public function setLayout($value = null) + { + $enum = array(self::LAYOUT_AUTO, self::LAYOUT_FIXED); + $this->layout = $this->setEnumVal($value, $enum, $this->layout); return $this; } @@ -631,4 +712,67 @@ class Table extends Border return $this; } + + /** + * Get position + * + * @return \PhpOffice\PhpWord\Style\TablePosition + */ + public function getPosition() + { + return $this->position; + } + + /** + * Set position + * + * @param mixed $value + * @return self + */ + public function setPosition($value = null) + { + $this->setObjectVal($value, 'TablePosition', $this->position); + + return $this; + } + + /** + * @return TblWidthComplexType + */ + public function getIndent() + { + return $this->indent; + } + + /** + * @param TblWidthComplexType $indent + * @return self + * @see http://www.datypic.com/sc/ooxml/e-w_tblInd-1.html + */ + public function setIndent(TblWidthComplexType $indent) + { + $this->indent = $indent; + + return $this; + } + + /** + * Get the columnWidths + * + * @return number[] + */ + public function getColumnWidths() + { + return $this->columnWidths; + } + + /** + * The column widths + * + * @param int[] $value + */ + public function setColumnWidths(array $value = null) + { + $this->columnWidths = $value; + } } diff --git a/src/PhpWord/Style/TablePosition.php b/src/PhpWord/Style/TablePosition.php new file mode 100644 index 00000000..d4b70831 --- /dev/null +++ b/src/PhpWord/Style/TablePosition.php @@ -0,0 +1,410 @@ +setStyleByArray($style); + } + + /** + * Get distance from left of table to text + * + * @return int + */ + public function getLeftFromText() + { + return $this->leftFromText; + } + + /** + * Set distance from left of table to text + * + * @param int $value + * @return self + */ + public function setLeftFromText($value = null) + { + $this->leftFromText = $this->setNumericVal($value, $this->leftFromText); + + return $this; + } + + /** + * Get distance from right of table to text + * + * @return int + */ + public function getRightFromText() + { + return $this->rightFromText; + } + + /** + * Set distance from right of table to text + * + * @param int $value + * @return self + */ + public function setRightFromText($value = null) + { + $this->rightFromText = $this->setNumericVal($value, $this->rightFromText); + + return $this; + } + + /** + * Get distance from top of table to text + * + * @return int + */ + public function getTopFromText() + { + return $this->topFromText; + } + + /** + * Set distance from top of table to text + * + * @param int $value + * @return self + */ + public function setTopFromText($value = null) + { + $this->topFromText = $this->setNumericVal($value, $this->topFromText); + + return $this; + } + + /** + * Get distance from bottom of table to text + * + * @return int + */ + public function getBottomFromText() + { + return $this->bottomFromText; + } + + /** + * Set distance from bottom of table to text + * + * @param int $value + * @return self + */ + public function setBottomFromText($value = null) + { + $this->bottomFromText = $this->setNumericVal($value, $this->bottomFromText); + + return $this; + } + + /** + * Get table vertical anchor + * + * @return string + */ + public function getVertAnchor() + { + return $this->vertAnchor; + } + + /** + * Set table vertical anchor + * + * @param string $value + * @return self + */ + public function setVertAnchor($value = null) + { + $enum = array( + self::VANCHOR_TEXT, + self::VANCHOR_MARGIN, + self::VANCHOR_PAGE, + ); + $this->vertAnchor = $this->setEnumVal($value, $enum, $this->vertAnchor); + + return $this; + } + + /** + * Get table horizontal anchor + * + * @return string + */ + public function getHorzAnchor() + { + return $this->horzAnchor; + } + + /** + * Set table horizontal anchor + * + * @param string $value + * @return self + */ + public function setHorzAnchor($value = null) + { + $enum = array( + self::HANCHOR_TEXT, + self::HANCHOR_MARGIN, + self::HANCHOR_PAGE, + ); + $this->horzAnchor = $this->setEnumVal($value, $enum, $this->horzAnchor); + + return $this; + } + + /** + * Get relative horizontal alignment from anchor + * + * @return string + */ + public function getTblpXSpec() + { + return $this->tblpXSpec; + } + + /** + * Set relative horizontal alignment from anchor + * + * @param string $value + * @return self + */ + public function setTblpXSpec($value = null) + { + $enum = array( + self::XALIGN_LEFT, + self::XALIGN_CENTER, + self::XALIGN_RIGHT, + self::XALIGN_INSIDE, + self::XALIGN_OUTSIDE, + ); + $this->tblpXSpec = $this->setEnumVal($value, $enum, $this->tblpXSpec); + + return $this; + } + + /** + * Get absolute horizontal distance from anchor + * + * @return int + */ + public function getTblpX() + { + return $this->tblpX; + } + + /** + * Set absolute horizontal distance from anchor + * + * @param int $value + * @return self + */ + public function setTblpX($value = null) + { + $this->tblpX = $this->setNumericVal($value, $this->tblpX); + + return $this; + } + + /** + * Get relative vertical alignment from anchor + * + * @return string + */ + public function getTblpYSpec() + { + return $this->tblpYSpec; + } + + /** + * Set relative vertical alignment from anchor + * + * @param string $value + * @return self + */ + public function setTblpYSpec($value = null) + { + $enum = array( + self::YALIGN_INLINE, + self::YALIGN_TOP, + self::YALIGN_CENTER, + self::YALIGN_BOTTOM, + self::YALIGN_INSIDE, + self::YALIGN_OUTSIDE, + ); + $this->tblpYSpec = $this->setEnumVal($value, $enum, $this->tblpYSpec); + + return $this; + } + + /** + * Get absolute vertical distance from anchor + * + * @return int + */ + public function getTblpY() + { + return $this->tblpY; + } + + /** + * Set absolute vertical distance from anchor + * + * @param int $value + * @return self + */ + public function setTblpY($value = null) + { + $this->tblpY = $this->setNumericVal($value, $this->tblpY); + + return $this; + } +} diff --git a/src/PhpWord/Style/TextBox.php b/src/PhpWord/Style/TextBox.php index 600fb8ea..e9c0f0c0 100644 --- a/src/PhpWord/Style/TextBox.php +++ b/src/PhpWord/Style/TextBox.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -70,7 +70,6 @@ class TextBox extends Image * Set margin top. * * @param int $value - * @return void */ public function setInnerMarginTop($value = null) { @@ -91,7 +90,6 @@ class TextBox extends Image * Set margin left. * * @param int $value - * @return void */ public function setInnerMarginLeft($value = null) { @@ -112,7 +110,6 @@ class TextBox extends Image * Set margin right. * * @param int $value - * @return void */ public function setInnerMarginRight($value = null) { @@ -133,7 +130,6 @@ class TextBox extends Image * Set margin bottom. * * @param int $value - * @return void */ public function setInnerMarginBottom($value = null) { @@ -154,7 +150,6 @@ class TextBox extends Image * Set TLRB cell margin. * * @param int $value Margin in twips - * @return void */ public function setInnerMargin($value = null) { @@ -167,7 +162,7 @@ class TextBox extends Image /** * Get cell margin * - * @return integer[] + * @return int[] */ public function getInnerMargin() { @@ -183,7 +178,8 @@ class TextBox extends Image { $hasInnerMargins = false; $margins = $this->getInnerMargin(); - for ($i = 0; $i < count($margins); $i++) { + $numMargins = count($margins); + for ($i = 0; $i < $numMargins; $i++) { if ($margins[$i] !== null) { $hasInnerMargins = true; } @@ -196,7 +192,6 @@ class TextBox extends Image * Set border size. * * @param int $value Size in points - * @return void */ public function setBorderSize($value = null) { @@ -217,7 +212,6 @@ class TextBox extends Image * Set border color. * * @param string $value - * @return void */ public function setBorderColor($value = null) { diff --git a/src/PhpWord/Template.php b/src/PhpWord/Template.php index 87ccd8ed..c42696f0 100644 --- a/src/PhpWord/Template.php +++ b/src/PhpWord/Template.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index e7a8d039..0d4bfdeb 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -10,20 +10,20 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord; +use PhpOffice\Common\Text; use PhpOffice\PhpWord\Escaper\RegExp; use PhpOffice\PhpWord\Escaper\Xml; use PhpOffice\PhpWord\Exception\CopyFileException; use PhpOffice\PhpWord\Exception\CreateTemporaryFileException; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Shared\ZipArchive; -use Zend\Stdlib\StringUtils; class TemplateProcessor { @@ -37,35 +37,35 @@ class TemplateProcessor protected $zipClass; /** - * @var string Temporary document filename (with path). + * @var string Temporary document filename (with path) */ protected $tempDocumentFilename; /** - * Content of main document part (in XML format) of the temporary document. + * Content of main document part (in XML format) of the temporary document * * @var string */ protected $tempDocumentMainPart; /** - * Content of headers (in XML format) of the temporary document. + * Content of headers (in XML format) of the temporary document * * @var string[] */ protected $tempDocumentHeaders = array(); /** - * Content of footers (in XML format) of the temporary document. + * Content of footers (in XML format) of the temporary document * * @var string[] */ protected $tempDocumentFooters = array(); /** - * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception. + * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception * - * @param string $documentTemplate The fully qualified template filename. + * @param string $documentTemplate The fully qualified template filename * * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException * @throws \PhpOffice\PhpWord\Exception\CopyFileException @@ -107,12 +107,13 @@ class TemplateProcessor * @param string $xml * @param \XSLTProcessor $xsltProcessor * - * @return string - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return string */ protected function transformSingleXml($xml, $xsltProcessor) { + libxml_disable_entity_loader(true); $domDocument = new \DOMDocument(); if (false === $domDocument->loadXML($xml)) { throw new Exception('Could not load the given XML document.'); @@ -147,7 +148,7 @@ class TemplateProcessor /** * Applies XSL style sheet to template's parts. - * + * * Note: since the method doesn't make any guess on logic of the provided XSL style sheet, * make sure that output is correctly escaped. Otherwise you may get broken document. * @@ -155,8 +156,6 @@ class TemplateProcessor * @param array $xslOptions * @param string $xslOptionsUri * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\Exception */ public function applyXslStyleSheet($xslDomDocument, $xslOptions = array(), $xslOptionsUri = '') @@ -194,7 +193,7 @@ class TemplateProcessor */ protected static function ensureUtf8Encoded($subject) { - if (!StringUtils::isValidUtf8($subject)) { + if (!Text::isUTF8($subject)) { $subject = utf8_encode($subject); } @@ -204,9 +203,7 @@ class TemplateProcessor /** * @param mixed $search * @param mixed $replace - * @param integer $limit - * - * @return void + * @param int $limit */ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) { @@ -220,10 +217,10 @@ class TemplateProcessor if (is_array($replace)) { foreach ($replace as &$item) { - $item = self::ensureUtf8Encoded($item); + $item = static::ensureUtf8Encoded($item); } } else { - $replace = self::ensureUtf8Encoded($replace); + $replace = static::ensureUtf8Encoded($replace); } if (Settings::isOutputEscapingEnabled()) { @@ -260,9 +257,7 @@ class TemplateProcessor * Clone a table row in a template document. * * @param string $search - * @param integer $numberOfClones - * - * @return void + * @param int $numberOfClones * * @throws \PhpOffice\PhpWord\Exception\Exception */ @@ -274,7 +269,7 @@ class TemplateProcessor $tagPos = strpos($this->tempDocumentMainPart, $search); if (!$tagPos) { - throw new Exception("Can not clone row, template variable not found or variable contains markup."); + throw new Exception('Can not clone row, template variable not found or variable contains markup.'); } $rowStart = $this->findRowStart($tagPos); @@ -319,8 +314,8 @@ class TemplateProcessor * Clone a block. * * @param string $blockname - * @param integer $clones - * @param boolean $replace + * @param int $clones + * @param bool $replace * * @return string|null */ @@ -328,7 +323,7 @@ class TemplateProcessor { $xmlBlock = null; preg_match( - '/(<\?xml.*)(\${' . $blockname . '}<\/w:.*?p>)(.*)()/is', + '/(<\?xml.*)(\${' . $blockname . '}<\/w:.*?p>)(.*)()/is', $this->tempDocumentMainPart, $matches ); @@ -357,8 +352,6 @@ class TemplateProcessor * * @param string $blockname * @param string $replacement - * - * @return void */ public function replaceBlock($blockname, $replacement) { @@ -381,8 +374,6 @@ class TemplateProcessor * Delete a block of text. * * @param string $blockname - * - * @return void */ public function deleteBlock($blockname) { @@ -392,9 +383,9 @@ class TemplateProcessor /** * Saves the result document. * - * @return string - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return string */ public function save() { @@ -422,8 +413,6 @@ class TemplateProcessor * @since 0.8.0 * * @param string $fileName - * - * @return void */ public function saveAs($fileName) { @@ -434,7 +423,7 @@ class TemplateProcessor } /* - * Note: we do not use `rename` function here, because it looses file ownership data on Windows platform. + * Note: we do not use `rename` function here, because it loses file ownership data on Windows platform. * As a result, user cannot open the file directly getting "Access denied" message. * * @see https://github.com/PHPOffice/PHPWord/issues/532 @@ -447,7 +436,7 @@ class TemplateProcessor * Finds parts of broken macros and sticks them together. * Macros, while being edited, could be implicitly broken by some of the word processors. * - * @param string $documentPart The document part in XML representation. + * @param string $documentPart The document part in XML representation * * @return string */ @@ -472,7 +461,7 @@ class TemplateProcessor * @param mixed $search * @param mixed $replace * @param string $documentPartXML - * @param integer $limit + * @param int $limit * * @return string */ @@ -481,10 +470,10 @@ class TemplateProcessor // Note: we can't use the same function for both cases here, because of performance considerations. if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) { return str_replace($search, $replace, $documentPartXML); - } else { - $regExpEscaper = new RegExp(); - return preg_replace($regExpEscaper->escape($search), $replace, $documentPartXML, $limit); } + $regExpEscaper = new RegExp(); + + return preg_replace($regExpEscaper->escape($search), $replace, $documentPartXML, $limit); } /** @@ -504,7 +493,7 @@ class TemplateProcessor /** * Get the name of the header file for $index. * - * @param integer $index + * @param int $index * * @return string */ @@ -514,17 +503,25 @@ class TemplateProcessor } /** + * Usually, the name of main part document will be 'document.xml'. However, some .docx files (possibly those from Office 365, experienced also on documents from Word Online created from blank templates) have file 'document22.xml' in their zip archive instead of 'document.xml'. This method searches content types file to correctly determine the file name. + * * @return string */ protected function getMainPartName() { - return 'word/document.xml'; + $contentTypes = $this->zipClass->getFromName('[Content_Types].xml'); + + $pattern = '~PartName="\/(word\/document.*?\.xml)" ContentType="application\/vnd\.openxmlformats-officedocument\.wordprocessingml\.document\.main\+xml"~'; + + preg_match($pattern, $contentTypes, $matches); + + return array_key_exists(1, $matches) ? $matches[1] : 'word/document.xml'; } /** * Get the name of the footer file for $index. * - * @param integer $index + * @param int $index * * @return string */ @@ -536,11 +533,11 @@ class TemplateProcessor /** * Find the start position of the nearest table row before $offset. * - * @param integer $offset - * - * @return integer + * @param int $offset * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return int */ protected function findRowStart($offset) { @@ -559,9 +556,9 @@ class TemplateProcessor /** * Find the end position of the nearest table row after $offset. * - * @param integer $offset + * @param int $offset * - * @return integer + * @return int */ protected function findRowEnd($offset) { @@ -571,8 +568,8 @@ class TemplateProcessor /** * Get a slice of a string. * - * @param integer $startPosition - * @param integer $endPosition + * @param int $startPosition + * @param int $endPosition * * @return string */ diff --git a/src/PhpWord/Writer/AbstractWriter.php b/src/PhpWord/Writer/AbstractWriter.php index 78ec5acd..7e0d511a 100644 --- a/src/PhpWord/Writer/AbstractWriter.php +++ b/src/PhpWord/Writer/AbstractWriter.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -96,17 +96,15 @@ abstract class AbstractWriter implements WriterInterface /** * Get PhpWord object * - * @return \PhpOffice\PhpWord\PhpWord - * * @throws \PhpOffice\PhpWord\Exception\Exception + * @return \PhpOffice\PhpWord\PhpWord */ public function getPhpWord() { if (!is_null($this->phpWord)) { return $this->phpWord; - } else { - throw new Exception("No PhpWord assigned."); } + throw new Exception('No PhpWord assigned.'); } /** @@ -118,6 +116,7 @@ abstract class AbstractWriter implements WriterInterface public function setPhpWord(PhpWord $phpWord = null) { $this->phpWord = $phpWord; + return $this; } @@ -131,9 +130,9 @@ abstract class AbstractWriter implements WriterInterface { if ($partName != '' && isset($this->writerParts[strtolower($partName)])) { return $this->writerParts[strtolower($partName)]; - } else { - return null; } + + return null; } /** @@ -152,9 +151,8 @@ abstract class AbstractWriter implements WriterInterface * @param bool $value * @param string $directory * - * @return self - * * @throws \PhpOffice\PhpWord\Exception\Exception + * @return self */ public function setUseDiskCaching($value = false, $directory = null) { @@ -218,15 +216,15 @@ abstract class AbstractWriter implements WriterInterface protected function getTempFile($filename) { // Temporary directory - $this->setTempDir(Settings::getTempDir() . '/PHPWordWriter/'); + $this->setTempDir(Settings::getTempDir() . uniqid('/PHPWordWriter_', true) . '/'); // Temporary file $this->originalFilename = $filename; if (strtolower($filename) == 'php://output' || strtolower($filename) == 'php://stdout') { $filename = tempnam(Settings::getTempDir(), 'PhpWord'); if (false === $filename) { - $filename = $this->originalFilename; - } + $filename = $this->originalFilename; // @codeCoverageIgnore + } // @codeCoverageIgnore } $this->tempFilename = $filename; @@ -236,8 +234,6 @@ abstract class AbstractWriter implements WriterInterface /** * Cleanup temporary file. * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\CopyFileException */ protected function cleanupTempFile() @@ -257,8 +253,6 @@ abstract class AbstractWriter implements WriterInterface /** * Clear temporary directory. - * - * @return void */ protected function clearTempDir() { @@ -272,9 +266,9 @@ abstract class AbstractWriter implements WriterInterface * * @param string $filename * - * @return \PhpOffice\PhpWord\Shared\ZipArchive - * * @throws \Exception + * + * @return \PhpOffice\PhpWord\Shared\ZipArchive */ protected function getZipArchive($filename) { @@ -305,9 +299,9 @@ abstract class AbstractWriter implements WriterInterface * * @param string $filename * - * @return resource - * * @throws \Exception + * + * @return resource */ protected function openFile($filename) { @@ -330,7 +324,6 @@ abstract class AbstractWriter implements WriterInterface * * @param resource $fileHandle * @param string $content - * @return void */ protected function writeFile($fileHandle, $content) { @@ -344,7 +337,6 @@ abstract class AbstractWriter implements WriterInterface * * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip * @param mixed $elements - * @return void */ protected function addFilesToPackage(ZipArchive $zip, $elements) { @@ -360,6 +352,10 @@ abstract class AbstractWriter implements WriterInterface // Retrive GD image content or get local media if (isset($element['isMemImage']) && $element['isMemImage']) { $image = call_user_func($element['createFunction'], $element['source']); + if ($element['imageType'] === 'image/png') { + // PNG images need to preserve alpha channel information + imagesavealpha($image, true); + } ob_start(); call_user_func($element['imageFunction'], $image); $imageContents = ob_get_contents(); @@ -380,7 +376,6 @@ abstract class AbstractWriter implements WriterInterface * @param \PhpOffice\PhpWord\Shared\ZipArchive $zipPackage * @param string $source * @param string $target - * @return void */ protected function addFileToPackage($zipPackage, $source, $target) { @@ -390,7 +385,7 @@ abstract class AbstractWriter implements WriterInterface $source = substr($source, 6); list($zipFilename, $imageFilename) = explode('#', $source); - $zip = new ZipArchive; + $zip = new ZipArchive(); if ($zip->open($zipFilename) !== false) { if ($zip->locateName($imageFilename)) { $zip->extractTo($this->getTempDir(), $imageFilename); @@ -411,17 +406,16 @@ abstract class AbstractWriter implements WriterInterface * Delete directory. * * @param string $dir - * @return void */ private function deleteDir($dir) { foreach (scandir($dir) as $file) { if ($file === '.' || $file === '..') { continue; - } elseif (is_file($dir . "/" . $file)) { - unlink($dir . "/" . $file); - } elseif (is_dir($dir . "/" . $file)) { - $this->deleteDir($dir . "/" . $file); + } elseif (is_file($dir . '/' . $file)) { + unlink($dir . '/' . $file); + } elseif (is_dir($dir . '/' . $file)) { + $this->deleteDir($dir . '/' . $file); } } diff --git a/src/PhpWord/Writer/HTML.php b/src/PhpWord/Writer/HTML.php index 5668f184..7f55b9d3 100644 --- a/src/PhpWord/Writer/HTML.php +++ b/src/PhpWord/Writer/HTML.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -30,7 +30,7 @@ class HTML extends AbstractWriter implements WriterInterface /** * Is the current writer creating PDF? * - * @var boolean + * @var bool */ protected $isPdf = false; @@ -65,8 +65,6 @@ class HTML extends AbstractWriter implements WriterInterface * * @param string $filename * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\Exception */ public function save($filename = null) @@ -119,7 +117,6 @@ class HTML extends AbstractWriter implements WriterInterface * * @param int $noteId * @param string $noteMark - * @return void */ public function addNote($noteId, $noteMark) { diff --git a/src/PhpWord/Writer/HTML/Element/AbstractElement.php b/src/PhpWord/Writer/HTML/Element/AbstractElement.php index 294d6de7..dc5ccfaa 100644 --- a/src/PhpWord/Writer/HTML/Element/AbstractElement.php +++ b/src/PhpWord/Writer/HTML/Element/AbstractElement.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -50,7 +50,7 @@ abstract class AbstractElement protected $withoutP = false; /** - * @var \Zend\Escaper\Escaper + * @var \Zend\Escaper\Escaper|\PhpOffice\PhpWord\Escaper\AbstractEscaper */ protected $escaper; @@ -78,7 +78,6 @@ abstract class AbstractElement * Set without paragraph. * * @param bool $value - * @return void */ public function setWithoutP($value) { diff --git a/src/PhpWord/Writer/HTML/Element/Bookmark.php b/src/PhpWord/Writer/HTML/Element/Bookmark.php new file mode 100644 index 00000000..082bd760 --- /dev/null +++ b/src/PhpWord/Writer/HTML/Element/Bookmark.php @@ -0,0 +1,45 @@ +element instanceof \PhpOffice\PhpWord\Element\Bookmark) { + return ''; + } + + $content = ''; + $content .= $this->writeOpening(); + $content .= "element->getName()}\"/>"; + $content .= $this->writeClosing(); + + return $content; + } +} diff --git a/src/PhpWord/Writer/HTML/Element/Container.php b/src/PhpWord/Writer/HTML/Element/Container.php index 88384a12..006b5889 100644 --- a/src/PhpWord/Writer/HTML/Element/Container.php +++ b/src/PhpWord/Writer/HTML/Element/Container.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/Endnote.php b/src/PhpWord/Writer/HTML/Element/Endnote.php index b049e437..2252dc3a 100644 --- a/src/PhpWord/Writer/HTML/Element/Endnote.php +++ b/src/PhpWord/Writer/HTML/Element/Endnote.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/Footnote.php b/src/PhpWord/Writer/HTML/Element/Footnote.php index b5aa0a0a..ed14db1e 100644 --- a/src/PhpWord/Writer/HTML/Element/Footnote.php +++ b/src/PhpWord/Writer/HTML/Element/Footnote.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/Image.php b/src/PhpWord/Writer/HTML/Element/Image.php index 9c69d41f..7c22a166 100644 --- a/src/PhpWord/Writer/HTML/Element/Image.php +++ b/src/PhpWord/Writer/HTML/Element/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -37,21 +37,16 @@ class Image extends Text if (!$this->element instanceof ImageElement) { return ''; } - /** @var \PhpOffice\PhpWord\Writer\HTML $parentWriter Type hint */ - $parentWriter = $this->parentWriter; - $content = ''; - if (!$parentWriter->isPdf()) { - $imageData = $this->element->getImageStringData(true); - if ($imageData !== null) { - $styleWriter = new ImageStyleWriter($this->element->getStyle()); - $style = $styleWriter->write(); - $imageData = 'data:' . $this->element->getImageType() . ';base64,' . $imageData; + $imageData = $this->element->getImageStringData(true); + if ($imageData !== null) { + $styleWriter = new ImageStyleWriter($this->element->getStyle()); + $style = $styleWriter->write(); + $imageData = 'data:' . $this->element->getImageType() . ';base64,' . $imageData; - $content .= $this->writeOpening(); - $content .= ""; - $content .= $this->writeClosing(); - } + $content .= $this->writeOpening(); + $content .= ""; + $content .= $this->writeClosing(); } return $content; diff --git a/src/PhpWord/Writer/HTML/Element/Link.php b/src/PhpWord/Writer/HTML/Element/Link.php index bff57cfc..f6dae5cd 100644 --- a/src/PhpWord/Writer/HTML/Element/Link.php +++ b/src/PhpWord/Writer/HTML/Element/Link.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -37,12 +37,12 @@ class Link extends Text return ''; } - $content = ''; - $content .= $this->writeOpening(); + $prefix = $this->element->isInternal() ? '#' : ''; + $content = $this->writeOpening(); if (Settings::isOutputEscapingEnabled()) { - $content .= "escaper->escapeHtmlAttr($this->element->getSource())}\">{$this->escaper->escapeHtml($this->element->getText())}"; + $content .= "escaper->escapeHtmlAttr($this->element->getSource())}\">{$this->escaper->escapeHtml($this->element->getText())}"; } else { - $content .= "element->getSource()}\">{$this->element->getText()}"; + $content .= "element->getSource()}\">{$this->element->getText()}"; } $content .= $this->writeClosing(); diff --git a/src/PhpWord/Writer/HTML/Element/ListItem.php b/src/PhpWord/Writer/HTML/Element/ListItem.php index d8b1e4ed..384b3ef1 100644 --- a/src/PhpWord/Writer/HTML/Element/ListItem.php +++ b/src/PhpWord/Writer/HTML/Element/ListItem.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/PageBreak.php b/src/PhpWord/Writer/HTML/Element/PageBreak.php index 8b332dcf..f9998e37 100644 --- a/src/PhpWord/Writer/HTML/Element/PageBreak.php +++ b/src/PhpWord/Writer/HTML/Element/PageBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -39,6 +39,6 @@ class PageBreak extends TextBreak return ''; } - return ""; + return ''; } } diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php index 9025f01a..a5143d2b 100644 --- a/src/PhpWord/Writer/HTML/Element/Table.php +++ b/src/PhpWord/Writer/HTML/Element/Table.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -39,19 +39,62 @@ class Table extends AbstractElement $rows = $this->element->getRows(); $rowCount = count($rows); if ($rowCount > 0) { - $content .= '' . PHP_EOL; - foreach ($rows as $row) { + $content .= 'element->getStyle()) . '>' . PHP_EOL; + + for ($i = 0; $i < $rowCount; $i++) { /** @var $row \PhpOffice\PhpWord\Element\Row Type hint */ - $rowStyle = $row->getStyle(); + $rowStyle = $rows[$i]->getStyle(); // $height = $row->getHeight(); $tblHeader = $rowStyle->isTblHeader(); $content .= '' . PHP_EOL; - foreach ($row->getCells() as $cell) { - $writer = new Container($this->parentWriter, $cell); - $cellTag = $tblHeader ? 'th' : 'td'; - $content .= "<{$cellTag}>" . PHP_EOL; - $content .= $writer->write(); - $content .= "" . PHP_EOL; + $rowCells = $rows[$i]->getCells(); + $rowCellCount = count($rowCells); + for ($j = 0; $j < $rowCellCount; $j++) { + $cellStyle = $rowCells[$j]->getStyle(); + $cellColSpan = $cellStyle->getGridSpan(); + $cellRowSpan = 1; + $cellVMerge = $cellStyle->getVMerge(); + // If this is the first cell of the vertical merge, find out how man rows it spans + if ($cellVMerge === 'restart') { + for ($k = $i + 1; $k < $rowCount; $k++) { + $kRowCells = $rows[$k]->getCells(); + if (isset($kRowCells[$j])) { + if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') { + $cellRowSpan++; + } else { + break; + } + } else { + break; + } + } + } + // Ignore cells that are merged vertically with previous rows + if ($cellVMerge !== 'continue') { + $cellTag = $tblHeader ? 'th' : 'td'; + $cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : ''); + $cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : ''); + $content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr}>" . PHP_EOL; + $writer = new Container($this->parentWriter, $rowCells[$j]); + $content .= $writer->write(); + if ($cellRowSpan > 1) { + // There shouldn't be any content in the subsequent merged cells, but lets check anyway + for ($k = $i + 1; $k < $rowCount; $k++) { + $kRowCells = $rows[$k]->getCells(); + if (isset($kRowCells[$j])) { + if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') { + $writer = new Container($this->parentWriter, $kRowCells[$j]); + $content .= $writer->write(); + } else { + break; + } + } else { + break; + } + } + } + $content .= "" . PHP_EOL; + } } $content .= '' . PHP_EOL; } @@ -60,4 +103,25 @@ class Table extends AbstractElement return $content; } + + /** + * Translates Table style in CSS equivalent + * + * @param \PhpOffice\PhpWord\Style\Table|null $tableStyle + * @return string + */ + private function getTableStyle(\PhpOffice\PhpWord\Style\Table $tableStyle = null) + { + if ($tableStyle == null) { + return ''; + } + $style = ' style="'; + if ($tableStyle->getLayout() == \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED) { + $style .= 'table-layout: fixed;'; + } elseif ($tableStyle->getLayout() == \PhpOffice\PhpWord\Style\Table::LAYOUT_AUTO) { + $style .= 'table-layout: auto;'; + } + + return $style . '"'; + } } diff --git a/src/PhpWord/Writer/HTML/Element/Text.php b/src/PhpWord/Writer/HTML/Element/Text.php index 87451595..04d76a83 100644 --- a/src/PhpWord/Writer/HTML/Element/Text.php +++ b/src/PhpWord/Writer/HTML/Element/Text.php @@ -10,13 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\HTML\Element; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; @@ -89,7 +90,6 @@ class Text extends AbstractElement * Set opening text. * * @param string $value - * @return void */ public function setOpeningText($value) { @@ -100,7 +100,6 @@ class Text extends AbstractElement * Set closing text. * * @param string $value - * @return void */ public function setClosingText($value) { @@ -123,6 +122,9 @@ class Text extends AbstractElement $content .= ""; } + //open track change tag + $content .= $this->writeTrackChangeOpening(); + return $content; } @@ -134,6 +136,10 @@ class Text extends AbstractElement protected function writeClosing() { $content = ''; + + //close track change tag + $content .= $this->writeTrackChangeClosing(); + if (!$this->withoutP) { if (Settings::isOutputEscapingEnabled()) { $content .= $this->escaper->escapeHtml($this->closingText); @@ -141,7 +147,64 @@ class Text extends AbstractElement $content .= $this->closingText; } - $content .= "

    " . PHP_EOL; + $content .= '

    ' . PHP_EOL; + } + + return $content; + } + + /** + * writes the track change opening tag + * + * @return string the HTML, an empty string if no track change information + */ + private function writeTrackChangeOpening() + { + $changed = $this->element->getTrackChange(); + if ($changed == null) { + return ''; + } + + $content = ''; + if (($changed->getChangeType() == TrackChange::INSERTED)) { + $content .= 'getChangeType() == TrackChange::DELETED) { + $content .= ' array('author'=> $changed->getAuthor(), 'id' => $this->element->getElementId())); + if ($changed->getDate() != null) { + $changedProp['changed']['date'] = $changed->getDate()->format('Y-m-d\TH:i:s\Z'); + } + $content .= json_encode($changedProp); + $content .= '\' '; + $content .= 'title="' . $changed->getAuthor(); + if ($changed->getDate() != null) { + $dateUser = $changed->getDate()->format('Y-m-d H:i:s'); + $content .= ' - ' . $dateUser; + } + $content .= '">'; + + return $content; + } + + /** + * writes the track change closing tag + * + * @return string the HTML, an empty string if no track change information + */ + private function writeTrackChangeClosing() + { + $changed = $this->element->getTrackChange(); + if ($changed == null) { + return ''; + } + + $content = ''; + if (($changed->getChangeType() == TrackChange::INSERTED)) { + $content .= ''; + } elseif ($changed->getChangeType() == TrackChange::DELETED) { + $content .= ''; } return $content; @@ -166,6 +229,8 @@ class Text extends AbstractElement if ($pStyleIsObject) { $styleWriter = new ParagraphStyleWriter($paragraphStyle); $style = $styleWriter->write(); + } elseif (is_string($paragraphStyle)) { + $style = $paragraphStyle; } if ($style) { $attribute = $pStyleIsObject ? 'style' : 'class'; @@ -177,8 +242,6 @@ class Text extends AbstractElement /** * Get font style. - * - * @return void */ private function getFontStyle() { @@ -190,11 +253,13 @@ class Text extends AbstractElement if ($fStyleIsObject) { $styleWriter = new FontStyleWriter($fontStyle); $style = $styleWriter->write(); + } elseif (is_string($fontStyle)) { + $style = $fontStyle; } if ($style) { $attribute = $fStyleIsObject ? 'style' : 'class'; $this->openingTags = ""; - $this->closingTags = ""; + $this->closingTags = ''; } } } diff --git a/src/PhpWord/Writer/HTML/Element/TextBreak.php b/src/PhpWord/Writer/HTML/Element/TextBreak.php index 9b23d739..6ff092db 100644 --- a/src/PhpWord/Writer/HTML/Element/TextBreak.php +++ b/src/PhpWord/Writer/HTML/Element/TextBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/TextRun.php b/src/PhpWord/Writer/HTML/Element/TextRun.php index 492f7597..b2deaf25 100644 --- a/src/PhpWord/Writer/HTML/Element/TextRun.php +++ b/src/PhpWord/Writer/HTML/Element/TextRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php index 23c29938..04ed61f5 100644 --- a/src/PhpWord/Writer/HTML/Element/Title.php +++ b/src/PhpWord/Writer/HTML/Element/Title.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -38,11 +38,17 @@ class Title extends AbstractElement } $tag = 'h' . $this->element->getDepth(); - if (Settings::isOutputEscapingEnabled()) { - $text = $this->escaper->escapeHtml($this->element->getText()); - } else { - $text = $this->element->getText(); + + $text = $this->element->getText(); + if (is_string($text)) { + if (Settings::isOutputEscapingEnabled()) { + $text = $this->escaper->escapeHtml($text); + } + } elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) { + $writer = new Container($this->parentWriter, $text); + $text = $writer->write(); } + $content = "<{$tag}>{$text}" . PHP_EOL; return $content; diff --git a/src/PhpWord/Writer/HTML/Part/AbstractPart.php b/src/PhpWord/Writer/HTML/Part/AbstractPart.php index 638f846b..2d86f399 100644 --- a/src/PhpWord/Writer/HTML/Part/AbstractPart.php +++ b/src/PhpWord/Writer/HTML/Part/AbstractPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -35,12 +35,12 @@ abstract class AbstractPart * @var \Zend\Escaper\Escaper */ protected $escaper; - + public function __construct() { $this->escaper = new Escaper(); } - + /** * @return string */ @@ -48,8 +48,6 @@ abstract class AbstractPart /** * @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer - * - * @return void */ public function setParentWriter(AbstractWriter $writer = null) { @@ -57,16 +55,15 @@ abstract class AbstractPart } /** - * @return \PhpOffice\PhpWord\Writer\AbstractWriter - * * @throws \PhpOffice\PhpWord\Exception\Exception + * + * @return \PhpOffice\PhpWord\Writer\AbstractWriter */ public function getParentWriter() { if ($this->parentWriter !== null) { return $this->parentWriter; - } else { - throw new Exception('No parent WriterInterface assigned.'); } + throw new Exception('No parent WriterInterface assigned.'); } } diff --git a/src/PhpWord/Writer/HTML/Part/Body.php b/src/PhpWord/Writer/HTML/Part/Body.php index 0d852a57..a029f965 100644 --- a/src/PhpWord/Writer/HTML/Part/Body.php +++ b/src/PhpWord/Writer/HTML/Part/Body.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -66,7 +66,7 @@ class Body extends AbstractPart $content = ''; if (!empty($notes)) { - $content .= "
    " . PHP_EOL; + $content .= '
    ' . PHP_EOL; foreach ($notes as $noteId => $noteMark) { list($noteType, $noteTypeId) = explode('-', $noteMark); $method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes'); diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php index fa4c3833..1107becf 100644 --- a/src/PhpWord/Writer/HTML/Part/Head.php +++ b/src/PhpWord/Writer/HTML/Part/Head.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -48,7 +48,7 @@ class Head extends AbstractPart 'keywords' => '', 'category' => '', 'company' => '', - 'manager' => '' + 'manager' => '', ); $title = $docProps->getTitle(); $title = ($title != '') ? $title : 'PHPWord'; @@ -60,11 +60,11 @@ class Head extends AbstractPart $content .= '' . $title . '' . PHP_EOL; foreach ($propertiesMapping as $key => $value) { $value = ($value == '') ? $key : $value; - $method = "get" . $key; + $method = 'get' . $key; if ($docProps->$method() != '') { $content .= '' . PHP_EOL; + . ' />' . PHP_EOL; } } $content .= $this->writeStyles(); @@ -86,22 +86,22 @@ class Head extends AbstractPart $defaultStyles = array( '*' => array( 'font-family' => Settings::getDefaultFontName(), - 'font-size' => Settings::getDefaultFontSize() . 'pt', + 'font-size' => Settings::getDefaultFontSize() . 'pt', ), 'a.NoteRef' => array( 'text-decoration' => 'none', ), 'hr' => array( - 'height' => '1px', - 'padding' => '0', - 'margin' => '1em 0', - 'border' => '0', + 'height' => '1px', + 'padding' => '0', + 'margin' => '1em 0', + 'border' => '0', 'border-top' => '1px solid #CCC', ), 'table' => array( - 'border' => '1px solid black', + 'border' => '1px solid black', 'border-spacing' => '0px', - 'width' => '100%', + 'width ' => '100%', ), 'td' => array( 'border' => '1px solid black', @@ -123,11 +123,11 @@ class Head extends AbstractPart } else { $name = '.' . $name; } - $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL; + $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL; } elseif ($style instanceof Paragraph) { $styleWriter = new ParagraphStyleWriter($style); $name = '.' . $name; - $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL; + $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL; } } } diff --git a/src/PhpWord/Writer/HTML/Style/AbstractStyle.php b/src/PhpWord/Writer/HTML/Style/AbstractStyle.php index 10a0a9ad..cfb54cb8 100644 --- a/src/PhpWord/Writer/HTML/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/HTML/Style/AbstractStyle.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -59,7 +59,6 @@ abstract class AbstractStyle * Set parent writer. * * @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer - * @return void */ public function setParentWriter($writer) { diff --git a/src/PhpWord/Writer/HTML/Style/Font.php b/src/PhpWord/Writer/HTML/Style/Font.php index c202af93..1aeaa347 100644 --- a/src/PhpWord/Writer/HTML/Style/Font.php +++ b/src/PhpWord/Writer/HTML/Style/Font.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -52,12 +52,17 @@ class Font extends AbstractStyle $css['background'] = $this->getValueIf($fgColor != '', $fgColor); $css['font-weight'] = $this->getValueIf($style->isBold(), 'bold'); $css['font-style'] = $this->getValueIf($style->isItalic(), 'italic'); - $css['vertical-align'] = $this->getValueIf($style->isSuperScript(), 'italic'); - $css['vertical-align'] = $this->getValueIf($style->isSuperScript(), 'super'); - $css['vertical-align'] = $this->getValueIf($style->isSubScript(), 'sub'); + $css['vertical-align'] = ''; + $css['vertical-align'] .= $this->getValueIf($style->isSuperScript(), 'super'); + $css['vertical-align'] .= $this->getValueIf($style->isSubScript(), 'sub'); $css['text-decoration'] = ''; $css['text-decoration'] .= $this->getValueIf($underline, 'underline '); $css['text-decoration'] .= $this->getValueIf($lineThrough, 'line-through '); + $css['text-transform'] = $this->getValueIf($style->isAllCaps(), 'uppercase'); + $css['font-variant'] = $this->getValueIf($style->isSmallCaps(), 'small-caps'); + + $spacing = $style->getSpacing(); + $css['letter-spacing'] = $this->getValueIf(!is_null($spacing), ($spacing / 20) . 'pt'); return $this->assembleCss($css); } diff --git a/src/PhpWord/Writer/HTML/Style/Generic.php b/src/PhpWord/Writer/HTML/Style/Generic.php index e3d2b352..ee5d0896 100644 --- a/src/PhpWord/Writer/HTML/Style/Generic.php +++ b/src/PhpWord/Writer/HTML/Style/Generic.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Style/Image.php b/src/PhpWord/Writer/HTML/Style/Image.php index 36a9feca..93747b46 100644 --- a/src/PhpWord/Writer/HTML/Style/Image.php +++ b/src/PhpWord/Writer/HTML/Style/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/HTML/Style/Paragraph.php b/src/PhpWord/Writer/HTML/Style/Paragraph.php index 593c6dca..863ef93b 100644 --- a/src/PhpWord/Writer/HTML/Style/Paragraph.php +++ b/src/PhpWord/Writer/HTML/Style/Paragraph.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -44,16 +44,9 @@ class Paragraph extends AbstractStyle $textAlign = ''; switch ($style->getAlignment()) { - case Jc::START: - case Jc::NUM_TAB: - case Jc::LEFT: - $textAlign = 'left'; - break; - case Jc::CENTER: $textAlign = 'center'; break; - case Jc::END: case Jc::MEDIUM_KASHIDA: case Jc::HIGH_KASHIDA: @@ -61,15 +54,13 @@ class Paragraph extends AbstractStyle case Jc::RIGHT: $textAlign = 'right'; break; - case Jc::BOTH: case Jc::DISTRIBUTE: case Jc::THAI_DISTRIBUTE: case Jc::JUSTIFY: $textAlign = 'justify'; break; - - default: + default: //all others, align left $textAlign = 'left'; break; } @@ -84,6 +75,9 @@ class Paragraph extends AbstractStyle $after = $spacing->getAfter(); $css['margin-top'] = $this->getValueIf(!is_null($before), ($before / 20) . 'pt'); $css['margin-bottom'] = $this->getValueIf(!is_null($after), ($after / 20) . 'pt'); + } else { + $css['margin-top'] = '0'; + $css['margin-bottom'] = '0'; } return $this->assembleCss($css); diff --git a/src/PhpWord/Writer/ODText.php b/src/PhpWord/Writer/ODText.php index 40bc6c2f..efd0d6a9 100644 --- a/src/PhpWord/Writer/ODText.php +++ b/src/PhpWord/Writer/ODText.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -63,7 +63,6 @@ class ODText extends AbstractWriter implements WriterInterface * Save PhpWord to file. * * @param string $filename - * @return void */ public function save($filename = null) { diff --git a/src/PhpWord/Writer/ODText/Element/AbstractElement.php b/src/PhpWord/Writer/ODText/Element/AbstractElement.php index 0ca43e4f..9c9fc1c4 100644 --- a/src/PhpWord/Writer/ODText/Element/AbstractElement.php +++ b/src/PhpWord/Writer/ODText/Element/AbstractElement.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Element/Container.php b/src/PhpWord/Writer/ODText/Element/Container.php index 212cd184..6ba8124f 100644 --- a/src/PhpWord/Writer/ODText/Element/Container.php +++ b/src/PhpWord/Writer/ODText/Element/Container.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Element/Image.php b/src/PhpWord/Writer/ODText/Element/Image.php index c6b16cfc..add45e10 100644 --- a/src/PhpWord/Writer/ODText/Element/Image.php +++ b/src/PhpWord/Writer/ODText/Element/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Element/Link.php b/src/PhpWord/Writer/ODText/Element/Link.php index cb0226a3..d6fec507 100644 --- a/src/PhpWord/Writer/ODText/Element/Link.php +++ b/src/PhpWord/Writer/ODText/Element/Link.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Element; -use PhpOffice\PhpWord\Settings; - /** * Text element writer * @@ -44,11 +42,7 @@ class Link extends AbstractElement $xmlWriter->startElement('text:a'); $xmlWriter->writeAttribute('xlink:type', 'simple'); $xmlWriter->writeAttribute('xlink:href', $element->getSource()); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($element->getText()); - } else { - $xmlWriter->writeRaw($element->getText()); - } + $this->writeText($element->getText()); $xmlWriter->endElement(); // text:a if (!$this->withoutP) { diff --git a/src/PhpWord/Writer/ODText/Element/PageBreak.php b/src/PhpWord/Writer/ODText/Element/PageBreak.php new file mode 100644 index 00000000..ecf47607 --- /dev/null +++ b/src/PhpWord/Writer/ODText/Element/PageBreak.php @@ -0,0 +1,36 @@ +getXmlWriter(); + + $xmlWriter->startElement('text:p'); + $xmlWriter->writeAttribute('text:style-name', 'P1'); + $xmlWriter->endElement(); + } +} diff --git a/src/PhpWord/Writer/ODText/Element/Table.php b/src/PhpWord/Writer/ODText/Element/Table.php index f6a2f845..088330ae 100644 --- a/src/PhpWord/Writer/ODText/Element/Table.php +++ b/src/PhpWord/Writer/ODText/Element/Table.php @@ -10,13 +10,17 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Element; +use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Element\Row as RowElement; +use PhpOffice\PhpWord\Element\Table as TableElement; + /** * Table element writer * @@ -36,32 +40,59 @@ class Table extends AbstractElement } $rows = $element->getRows(); $rowCount = count($rows); - $colCount = $element->countColumns(); if ($rowCount > 0) { $xmlWriter->startElement('table:table'); $xmlWriter->writeAttribute('table:name', $element->getElementId()); $xmlWriter->writeAttribute('table:style', $element->getElementId()); - $xmlWriter->startElement('table:table-column'); - $xmlWriter->writeAttribute('table:number-columns-repeated', $colCount); - $xmlWriter->endElement(); // table:table-column + // Write columns + $this->writeColumns($xmlWriter, $element); + // Write rows foreach ($rows as $row) { - $xmlWriter->startElement('table:table-row'); - /** @var $row \PhpOffice\PhpWord\Element\Row Type hint */ - foreach ($row->getCells() as $cell) { - $xmlWriter->startElement('table:table-cell'); - $xmlWriter->writeAttribute('office:value-type', 'string'); - - $containerWriter = new Container($xmlWriter, $cell); - $containerWriter->write(); - - $xmlWriter->endElement(); // table:table-cell - } - $xmlWriter->endElement(); // table:table-row + $this->writeRow($xmlWriter, $row); } $xmlWriter->endElement(); // table:table } } + + /** + * Write column. + * + * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Element\Table $element + */ + private function writeColumns(XMLWriter $xmlWriter, TableElement $element) + { + $colCount = $element->countColumns(); + + for ($i = 0; $i < $colCount; $i++) { + $xmlWriter->startElement('table:table-column'); + $xmlWriter->writeAttribute('table:style-name', $element->getElementId() . '.' . $i); + $xmlWriter->endElement(); + } + } + + /** + * Write row. + * + * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Element\Row $row + */ + private function writeRow(XMLWriter $xmlWriter, RowElement $row) + { + $xmlWriter->startElement('table:table-row'); + /** @var $row \PhpOffice\PhpWord\Element\Row Type hint */ + foreach ($row->getCells() as $cell) { + $xmlWriter->startElement('table:table-cell'); + $xmlWriter->writeAttribute('office:value-type', 'string'); + + $containerWriter = new Container($xmlWriter, $cell); + $containerWriter->write(); + + $xmlWriter->endElement(); // table:table-cell + } + $xmlWriter->endElement(); // table:table-row + } } diff --git a/src/PhpWord/Writer/ODText/Element/Text.php b/src/PhpWord/Writer/ODText/Element/Text.php index cff68481..7dcd28a0 100644 --- a/src/PhpWord/Writer/ODText/Element/Text.php +++ b/src/PhpWord/Writer/ODText/Element/Text.php @@ -10,15 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Element; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\Exception\Exception; -use PhpOffice\PhpWord\Settings; /** * Text element writer @@ -47,21 +47,25 @@ class Text extends AbstractElement if ($fStyleIsObject) { // Don't never be the case, because I browse all sections for cleaning all styles not declared throw new Exception('PhpWord : $fStyleIsObject wouldn\'t be an object'); + } + + if (!$this->withoutP) { + $xmlWriter->startElement('text:p'); // text:p + } + if ($element->getTrackChange() != null && $element->getTrackChange()->getChangeType() == TrackChange::DELETED) { + $xmlWriter->startElement('text:change'); + $xmlWriter->writeAttribute('text:change-id', $element->getTrackChange()->getElementId()); + $xmlWriter->endElement(); } else { - if (!$this->withoutP) { - $xmlWriter->startElement('text:p'); // text:p - } if (empty($fontStyle)) { if (empty($paragraphStyle)) { $xmlWriter->writeAttribute('text:style-name', 'P1'); } elseif (is_string($paragraphStyle)) { $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); } - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($element->getText()); - } else { - $xmlWriter->writeRaw($element->getText()); - } + $this->writeChangeInsertion(true, $element->getTrackChange()); + $this->writeText($element->getText()); + $this->writeChangeInsertion(false, $element->getTrackChange()); } else { if (empty($paragraphStyle)) { $xmlWriter->writeAttribute('text:style-name', 'Standard'); @@ -73,16 +77,25 @@ class Text extends AbstractElement if (is_string($fontStyle)) { $xmlWriter->writeAttribute('text:style-name', $fontStyle); } - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($element->getText()); - } else { - $xmlWriter->writeRaw($element->getText()); - } + $this->writeChangeInsertion(true, $element->getTrackChange()); + $this->writeText($element->getText()); + $this->writeChangeInsertion(false, $element->getTrackChange()); $xmlWriter->endElement(); } - if (!$this->withoutP) { - $xmlWriter->endElement(); // text:p - } + } + if (!$this->withoutP) { + $xmlWriter->endElement(); // text:p } } + + private function writeChangeInsertion($start = true, TrackChange $trackChange = null) + { + if ($trackChange == null || $trackChange->getChangeType() != TrackChange::INSERTED) { + return; + } + $xmlWriter = $this->getXmlWriter(); + $xmlWriter->startElement('text:change-' . ($start ? 'start' : 'end')); + $xmlWriter->writeAttribute('text:change-id', $trackChange->getElementId()); + $xmlWriter->endElement(); + } } diff --git a/src/PhpWord/Writer/ODText/Element/TextBreak.php b/src/PhpWord/Writer/ODText/Element/TextBreak.php index b0f5009e..80cd1387 100644 --- a/src/PhpWord/Writer/ODText/Element/TextBreak.php +++ b/src/PhpWord/Writer/ODText/Element/TextBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Element/TextRun.php b/src/PhpWord/Writer/ODText/Element/TextRun.php index 03717016..78e5a8ad 100644 --- a/src/PhpWord/Writer/ODText/Element/TextRun.php +++ b/src/PhpWord/Writer/ODText/Element/TextRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Element/Title.php b/src/PhpWord/Writer/ODText/Element/Title.php index b20ba944..8b9440ab 100644 --- a/src/PhpWord/Writer/ODText/Element/Title.php +++ b/src/PhpWord/Writer/ODText/Element/Title.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Element; -use PhpOffice\PhpWord\Settings; - /** * Title element writer * @@ -39,10 +37,12 @@ class Title extends AbstractElement $xmlWriter->startElement('text:h'); $xmlWriter->writeAttribute('text:outline-level', $element->getDepth()); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($element->getText()); - } else { - $xmlWriter->writeRaw($element->getText()); + $text = $element->getText(); + if (is_string($text)) { + $this->writeText($text); + } elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) { + $containerWriter = new Container($xmlWriter, $text); + $containerWriter->write(); } $xmlWriter->endElement(); // text:h } diff --git a/src/PhpWord/Writer/ODText/Part/AbstractPart.php b/src/PhpWord/Writer/ODText/Part/AbstractPart.php index dc377e0f..f2844de6 100644 --- a/src/PhpWord/Writer/ODText/Part/AbstractPart.php +++ b/src/PhpWord/Writer/ODText/Part/AbstractPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -37,7 +37,6 @@ abstract class AbstractPart extends Word2007AbstractPart * Write common root attributes. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ protected function writeCommonRootAttributes(XMLWriter $xmlWriter) { @@ -74,7 +73,6 @@ abstract class AbstractPart extends Word2007AbstractPart * Write font faces declaration. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ protected function writeFontFaces(XMLWriter $xmlWriter) { diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 61f8e7e2..99ee9353 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -10,18 +10,20 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Element\AbstractContainer; use PhpOffice\PhpWord\Element\Image; use PhpOffice\PhpWord\Element\Table; use PhpOffice\PhpWord\Element\Text; use PhpOffice\PhpWord\Element\TextRun; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; @@ -74,6 +76,40 @@ class Content extends AbstractPart $xmlWriter->startElement('office:body'); $xmlWriter->startElement('office:text'); + // Tracked changes declarations + $trackedChanges = array(); + $sections = $phpWord->getSections(); + foreach ($sections as $section) { + $this->collectTrackedChanges($section, $trackedChanges); + } + $xmlWriter->startElement('text:tracked-changes'); + foreach ($trackedChanges as $trackedElement) { + $trackedChange = $trackedElement->getTrackChange(); + $xmlWriter->startElement('text:changed-region'); + $trackedChange->setElementId(); + $xmlWriter->writeAttribute('text:id', $trackedChange->getElementId()); + + if (($trackedChange->getChangeType() == TrackChange::INSERTED)) { + $xmlWriter->startElement('text:insertion'); + } elseif ($trackedChange->getChangeType() == TrackChange::DELETED) { + $xmlWriter->startElement('text:deletion'); + } + + $xmlWriter->startElement('office:change-info'); + $xmlWriter->writeElement('dc:creator', $trackedChange->getAuthor()); + if ($trackedChange->getDate() != null) { + $xmlWriter->writeElement('dc:date', $trackedChange->getDate()->format('Y-m-d\TH:i:s\Z')); + } + $xmlWriter->endElement(); // office:change-info + if ($trackedChange->getChangeType() == TrackChange::DELETED) { + $xmlWriter->writeElement('text:p', $trackedElement->getText()); + } + + $xmlWriter->endElement(); // text:insertion|text:deletion + $xmlWriter->endElement(); // text:changed-region + } + $xmlWriter->endElement(); // text:tracked-changes + // Sequence declarations $sequences = array('Illustration', 'Table', 'Text', 'Drawing'); $xmlWriter->startElement('text:sequence-decls'); @@ -111,7 +147,6 @@ class Content extends AbstractPart * @since 0.11.0 * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeAutoStyles(XMLWriter $xmlWriter) { @@ -121,7 +156,6 @@ class Content extends AbstractPart foreach ($this->autoStyles as $element => $styles) { $writerClass = 'PhpOffice\\PhpWord\\Writer\\ODText\\Style\\' . $element; foreach ($styles as $style) { - /** @var \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle $styleWriter Type hint */ $styleWriter = new $writerClass($xmlWriter, $style); $styleWriter->write(); @@ -135,7 +169,6 @@ class Content extends AbstractPart * Write automatic styles. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeTextStyles(XMLWriter $xmlWriter) { @@ -169,7 +202,6 @@ class Content extends AbstractPart * Get automatic styles. * * @param \PhpOffice\PhpWord\PhpWord $phpWord - * @return void */ private function getAutoStyles(PhpWord $phpWord) { @@ -190,9 +222,8 @@ class Content extends AbstractPart * Table style can be null or string of the style name * * @param \PhpOffice\PhpWord\Element\AbstractContainer $container - * @param int &$paragraphStyleCount - * @param int &$fontStyleCount - * @return void + * @param int $paragraphStyleCount + * @param int $fontStyleCount * @todo Simplify the logic */ private function getContainerStyle($container, &$paragraphStyleCount, &$fontStyleCount) @@ -208,6 +239,7 @@ class Content extends AbstractPart $style->setStyleName('fr' . $element->getMediaIndex()); $this->autoStyles['Image'][] = $style; } elseif ($element instanceof Table) { + /** @var \PhpOffice\PhpWord\Style\Table $style */ $style = $element->getStyle(); if ($style === null) { $style = new TableStyle(); @@ -215,6 +247,7 @@ class Content extends AbstractPart $style = Style::getStyle($style); } $style->setStyleName($element->getElementId()); + $style->setColumnWidths($element->findFirstDefinedCellWidths()); $this->autoStyles['Table'][] = $style; } } @@ -223,10 +256,9 @@ class Content extends AbstractPart /** * Get style of individual element * - * @param \PhpOffice\PhpWord\Element\Text &$element - * @param int &$paragraphStyleCount - * @param int &$fontStyleCount - * @return void + * @param \PhpOffice\PhpWord\Element\Text $element + * @param int $paragraphStyleCount + * @param int $fontStyleCount */ private function getElementStyle(&$element, &$paragraphStyleCount, &$fontStyleCount) { @@ -234,19 +266,37 @@ class Content extends AbstractPart $paragraphStyle = $element->getParagraphStyle(); $phpWord = $this->getParentWriter()->getPhpWord(); - // Font if ($fontStyle instanceof Font) { + // Font $fontStyleCount++; $style = $phpWord->addFontStyle("T{$fontStyleCount}", $fontStyle); $style->setAuto(); $element->setFontStyle("T{$fontStyleCount}"); - - // Paragraph } elseif ($paragraphStyle instanceof Paragraph) { + // Paragraph $paragraphStyleCount++; $style = $phpWord->addParagraphStyle("P{$paragraphStyleCount}", array()); $style->setAuto(); $element->setParagraphStyle("P{$paragraphStyleCount}"); } } + + /** + * Finds all tracked changes + * + * @param AbstractContainer $container + * @param \PhpOffice\PhpWord\Element\AbstractElement[] $trackedChanges + */ + private function collectTrackedChanges(AbstractContainer $container, &$trackedChanges = array()) + { + $elements = $container->getElements(); + foreach ($elements as $element) { + if ($element->getTrackChange() != null) { + $trackedChanges[] = $element; + } + if (is_callable(array($element, 'getElements'))) { + $this->collectTrackedChanges($element, $trackedChanges); + } + } + } } diff --git a/src/PhpWord/Writer/ODText/Part/Manifest.php b/src/PhpWord/Writer/ODText/Part/Manifest.php index 237c1a11..f952b4c0 100644 --- a/src/PhpWord/Writer/ODText/Part/Manifest.php +++ b/src/PhpWord/Writer/ODText/Part/Manifest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Part/Meta.php b/src/PhpWord/Writer/ODText/Part/Meta.php index f16db161..f38ad01d 100644 --- a/src/PhpWord/Writer/ODText/Part/Meta.php +++ b/src/PhpWord/Writer/ODText/Part/Meta.php @@ -10,15 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\Common\XMLWriter; -use PhpOffice\PhpWord\Settings; /** * ODText meta part writer: meta.xml @@ -90,7 +89,6 @@ class Meta extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $property * @param string $value - * @return void * * @todo Handle other `$type`: double|date|dateTime|duration|boolean (4th arguments) */ @@ -101,11 +99,7 @@ class Meta extends AbstractPart // if ($type !== null) { // $xmlWriter->writeAttribute('meta:value-type', $type); // } - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($value); - } else { - $xmlWriter->writeRaw($value); - } + $this->writeText($value); $xmlWriter->endElement(); // meta:user-defined } } diff --git a/src/PhpWord/Writer/ODText/Part/Mimetype.php b/src/PhpWord/Writer/ODText/Part/Mimetype.php index 7cf78b4b..552f5440 100644 --- a/src/PhpWord/Writer/ODText/Part/Mimetype.php +++ b/src/PhpWord/Writer/ODText/Part/Mimetype.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Part/Styles.php b/src/PhpWord/Writer/ODText/Part/Styles.php index b50be0e8..e7635e98 100644 --- a/src/PhpWord/Writer/ODText/Part/Styles.php +++ b/src/PhpWord/Writer/ODText/Part/Styles.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -52,8 +52,10 @@ class Styles extends AbstractPart // Automatic styles $xmlWriter->startElement('office:automatic-styles'); $this->writePageLayout($xmlWriter); + $xmlWriter->endElement(); // office:automatic-styles + + // Master style $this->writeMaster($xmlWriter); - $xmlWriter->endElement(); $xmlWriter->endElement(); // office:document-styles @@ -64,7 +66,6 @@ class Styles extends AbstractPart * Write default styles. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeDefault(XMLWriter $xmlWriter) { @@ -81,22 +82,27 @@ class Styles extends AbstractPart $xmlWriter->writeAttribute('style:writing-mode', 'page'); $xmlWriter->endElement(); // style:paragraph-properties + $language = $this->getParentWriter()->getPhpWord()->getSettings()->getThemeFontLang(); + $latinLang = $language != null && is_string($language->getLatin()) ? explode('-', $language->getLatin()) : array('fr', 'FR'); + $asianLang = $language != null && is_string($language->getEastAsia()) ? explode('-', $language->getEastAsia()) : array('zh', 'CN'); + $complexLang = $language != null && is_string($language->getBidirectional()) ? explode('-', $language->getBidirectional()) : array('hi', 'IN'); + // Font $xmlWriter->startElement('style:text-properties'); $xmlWriter->writeAttribute('style:use-window-font-color', 'true'); $xmlWriter->writeAttribute('style:font-name', Settings::getDefaultFontName()); $xmlWriter->writeAttribute('fo:font-size', Settings::getDefaultFontSize() . 'pt'); - $xmlWriter->writeAttribute('fo:language', 'fr'); - $xmlWriter->writeAttribute('fo:country', 'FR'); + $xmlWriter->writeAttribute('fo:language', $latinLang[0]); + $xmlWriter->writeAttribute('fo:country', $latinLang[1]); $xmlWriter->writeAttribute('style:letter-kerning', 'true'); $xmlWriter->writeAttribute('style:font-name-asian', Settings::getDefaultFontName() . '2'); $xmlWriter->writeAttribute('style:font-size-asian', Settings::getDefaultFontSize() . 'pt'); - $xmlWriter->writeAttribute('style:language-asian', 'zh'); - $xmlWriter->writeAttribute('style:country-asian', 'CN'); + $xmlWriter->writeAttribute('style:language-asian', $asianLang[0]); + $xmlWriter->writeAttribute('style:country-asian', $asianLang[1]); $xmlWriter->writeAttribute('style:font-name-complex', Settings::getDefaultFontName() . '2'); $xmlWriter->writeAttribute('style:font-size-complex', Settings::getDefaultFontSize() . 'pt'); - $xmlWriter->writeAttribute('style:language-complex', 'hi'); - $xmlWriter->writeAttribute('style:country-complex', 'IN'); + $xmlWriter->writeAttribute('style:language-complex', $complexLang[0]); + $xmlWriter->writeAttribute('style:country-complex', $complexLang[1]); $xmlWriter->writeAttribute('fo:hyphenate', 'false'); $xmlWriter->writeAttribute('fo:hyphenation-remain-char-count', '2'); $xmlWriter->writeAttribute('fo:hyphenation-push-char-count', '2'); @@ -109,7 +115,6 @@ class Styles extends AbstractPart * Write named styles. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeNamed(XMLWriter $xmlWriter) { @@ -127,11 +132,11 @@ class Styles extends AbstractPart } } } + /** * Write page layout styles. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writePageLayout(XMLWriter $xmlWriter) { @@ -139,7 +144,7 @@ class Styles extends AbstractPart $xmlWriter->writeAttribute('style:name', 'Mpm1'); $xmlWriter->startElement('style:page-layout-properties'); - $xmlWriter->writeAttribute('fo:page-width', "21.001cm"); + $xmlWriter->writeAttribute('fo:page-width', '21.001cm'); $xmlWriter->writeAttribute('fo:page-height', '29.7cm'); $xmlWriter->writeAttribute('style:num-format', '1'); $xmlWriter->writeAttribute('style:print-orientation', 'portrait'); @@ -170,7 +175,6 @@ class Styles extends AbstractPart $xmlWriter->endElement(); // style:page-layout-properties - $xmlWriter->startElement('style:header-style'); $xmlWriter->endElement(); // style:header-style @@ -184,7 +188,6 @@ class Styles extends AbstractPart * Write master style. * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeMaster(XMLWriter $xmlWriter) { diff --git a/src/PhpWord/Writer/ODText/Style/AbstractStyle.php b/src/PhpWord/Writer/ODText/Style/AbstractStyle.php index 7bc49cb3..f7679ab2 100644 --- a/src/PhpWord/Writer/ODText/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/ODText/Style/AbstractStyle.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/ODText/Style/Font.php b/src/PhpWord/Writer/ODText/Style/Font.php index 5d8e5753..7c7d20dd 100644 --- a/src/PhpWord/Writer/ODText/Style/Font.php +++ b/src/PhpWord/Writer/ODText/Style/Font.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Font extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/ODText/Style/Image.php b/src/PhpWord/Writer/ODText/Style/Image.php index 447f449c..13005a7f 100644 --- a/src/PhpWord/Writer/ODText/Style/Image.php +++ b/src/PhpWord/Writer/ODText/Style/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Image extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/ODText/Style/Paragraph.php b/src/PhpWord/Writer/ODText/Style/Paragraph.php index 1d821810..223d02f0 100644 --- a/src/PhpWord/Writer/ODText/Style/Paragraph.php +++ b/src/PhpWord/Writer/ODText/Style/Paragraph.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Paragraph extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -37,8 +35,8 @@ class Paragraph extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $marginTop = is_null($style->getSpaceBefore()) ? '0' : round(17.6 / $style->getSpaceBefore(), 2); - $marginBottom = is_null($style->getSpaceAfter()) ? '0' : round(17.6 / $style->getSpaceAfter(), 2); + $marginTop = (is_null($style->getSpaceBefore()) || $style->getSpaceBefore() == 0) ? '0' : round(17.6 / $style->getSpaceBefore(), 2); + $marginBottom = (is_null($style->getSpaceAfter()) || $style->getSpaceAfter() == 0) ? '0' : round(17.6 / $style->getSpaceAfter(), 2); $xmlWriter->startElement('style:style'); $xmlWriter->writeAttribute('style:name', $style->getStyleName()); diff --git a/src/PhpWord/Writer/ODText/Style/Section.php b/src/PhpWord/Writer/ODText/Style/Section.php index 79d57adb..92d88911 100644 --- a/src/PhpWord/Writer/ODText/Style/Section.php +++ b/src/PhpWord/Writer/ODText/Style/Section.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Section extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -40,7 +38,7 @@ class Section extends AbstractStyle $xmlWriter->startElement('style:style'); $xmlWriter->writeAttribute('style:name', $style->getStyleName()); - $xmlWriter->writeAttribute('style:family', "section"); + $xmlWriter->writeAttribute('style:family', 'section'); $xmlWriter->startElement('style:section-properties'); $xmlWriter->startElement('style:columns'); diff --git a/src/PhpWord/Writer/ODText/Style/Table.php b/src/PhpWord/Writer/ODText/Style/Table.php index ff3cc423..5ddee25a 100644 --- a/src/PhpWord/Writer/ODText/Style/Table.php +++ b/src/PhpWord/Writer/ODText/Style/Table.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Table extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -47,5 +45,19 @@ class Table extends AbstractStyle $xmlWriter->writeAttribute('table:align', 'center'); $xmlWriter->endElement(); // style:table-properties $xmlWriter->endElement(); // style:style + + $cellWidths = $style->getColumnWidths(); + $countCellWidths = count($cellWidths); + + for ($i = 0; $i < $countCellWidths; $i++) { + $width = $cellWidths[$i]; + $xmlWriter->startElement('style:style'); + $xmlWriter->writeAttribute('style:name', $style->getStyleName() . '.' . $i); + $xmlWriter->writeAttribute('style:family', 'table-column'); + $xmlWriter->startElement('style:table-column-properties'); + $xmlWriter->writeAttribute('style:column-width', number_format($width * 0.0017638889, 2, '.', '') . 'cm'); + $xmlWriter->endElement(); // style:table-column-properties + $xmlWriter->endElement(); // style:style + } } } diff --git a/src/PhpWord/Writer/PDF.php b/src/PhpWord/Writer/PDF.php index 5e5d9d71..64dcc789 100644 --- a/src/PhpWord/Writer/PDF.php +++ b/src/PhpWord/Writer/PDF.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PhpWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PhpWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -47,7 +47,7 @@ class PDF $pdfLibraryName = Settings::getPdfRendererName(); $pdfLibraryPath = Settings::getPdfRendererPath(); if (is_null($pdfLibraryName) || is_null($pdfLibraryPath)) { - throw new Exception("PDF rendering library or library path has not been defined."); + throw new Exception('PDF rendering library or library path has not been defined.'); } $includePath = str_replace('\\', '/', get_include_path()); diff --git a/src/PhpWord/Writer/PDF/AbstractRenderer.php b/src/PhpWord/Writer/PDF/AbstractRenderer.php index 2778aa52..5f9e3b3a 100644 --- a/src/PhpWord/Writer/PDF/AbstractRenderer.php +++ b/src/PhpWord/Writer/PDF/AbstractRenderer.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PhpWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PhpWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -83,15 +83,17 @@ abstract class AbstractRenderer extends HTML public function __construct(PhpWord $phpWord) { parent::__construct($phpWord); - $includeFile = Settings::getPdfRendererPath() . '/' . $this->includeFile; - if (file_exists($includeFile)) { - /** @noinspection PhpIncludeInspection Dynamic includes */ - require_once $includeFile; - } else { - // @codeCoverageIgnoreStart - // Can't find any test case. Uncomment when found. - throw new Exception('Unable to load PDF Rendering library'); - // @codeCoverageIgnoreEnd + if ($this->includeFile != null) { + $includeFile = Settings::getPdfRendererPath() . '/' . $this->includeFile; + if (file_exists($includeFile)) { + /** @noinspection PhpIncludeInspection Dynamic includes */ + require_once $includeFile; + } else { + // @codeCoverageIgnoreStart + // Can't find any test case. Uncomment when found. + throw new Exception('Unable to load PDF Rendering library'); + // @codeCoverageIgnoreEnd + } } } @@ -141,6 +143,7 @@ abstract class AbstractRenderer extends HTML public function setPaperSize($value = 9) { $this->paperSize = $value; + return $this; } @@ -163,6 +166,7 @@ abstract class AbstractRenderer extends HTML public function setOrientation($value = 'default') { $this->orientation = $value; + return $this; } @@ -171,9 +175,8 @@ abstract class AbstractRenderer extends HTML * * @param string $filename Name of the file to save as * - * @return resource - * * @throws \PhpOffice\PhpWord\Exception\Exception + * @return resource */ protected function prepareForSave($filename = null) { @@ -194,8 +197,6 @@ abstract class AbstractRenderer extends HTML * * @param resource $fileHandle * - * @return void - * * @throws Exception */ protected function restoreStateAfterSave($fileHandle) diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php index e31f3aae..5fa8f75d 100644 --- a/src/PhpWord/Writer/PDF/DomPDF.php +++ b/src/PhpWord/Writer/PDF/DomPDF.php @@ -10,19 +10,20 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PhpWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PhpWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\PDF; +use Dompdf\Dompdf as DompdfLib; use PhpOffice\PhpWord\Writer\WriterInterface; /** * DomPDF writer * - * @link https://github.com/dompdf/dompdf + * @see https://github.com/dompdf/dompdf * @since 0.10.0 */ class DomPDF extends AbstractRenderer implements WriterInterface @@ -32,13 +33,12 @@ class DomPDF extends AbstractRenderer implements WriterInterface * * @var string */ - protected $includeFile = 'dompdf_config.inc.php'; + protected $includeFile = null; /** * Save PhpWord to file. * * @param string $filename Name of the file to save as - * @return void */ public function save($filename = null) { @@ -49,9 +49,9 @@ class DomPDF extends AbstractRenderer implements WriterInterface $orientation = 'portrait'; // Create PDF - $pdf = new \DOMPDF(); - $pdf->set_paper(strtolower($paperSize), $orientation); - $pdf->load_html($this->getContent()); + $pdf = new DompdfLib(); + $pdf->setPaper(strtolower($paperSize), $orientation); + $pdf->loadHtml(str_replace(PHP_EOL, '', $this->getContent())); $pdf->render(); // Write to file diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php index 028ffac7..e63f5dfd 100644 --- a/src/PhpWord/Writer/PDF/MPDF.php +++ b/src/PhpWord/Writer/PDF/MPDF.php @@ -10,35 +10,44 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PhpWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PhpWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\PDF; +use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\WriterInterface; /** * MPDF writer * - * @link http://www.mpdf1.com/ + * @see http://www.mpdf1.com/ * @since 0.11.0 */ class MPDF extends AbstractRenderer implements WriterInterface { /** - * Name of renderer include file + * Overridden to set the correct includefile, only needed for MPDF 5 * - * @var string + * @codeCoverageIgnore + * @param PhpWord $phpWord */ - protected $includeFile = 'mpdf.php'; + public function __construct(PhpWord $phpWord) + { + if (file_exists(Settings::getPdfRendererPath() . '/mpdf.php')) { + // MPDF version 5.* needs this file to be included, later versions not + $this->includeFile = 'mpdf.php'; + } + parent::__construct($phpWord); + } /** * Save PhpWord to file. * * @param string $filename Name of the file to save as - * @return void */ public function save($filename = null) { @@ -49,7 +58,8 @@ class MPDF extends AbstractRenderer implements WriterInterface $orientation = strtoupper('portrait'); // Create PDF - $pdf = new \mpdf(); + $mPdfClass = $this->getMPdfClassName(); + $pdf = new $mPdfClass(); $pdf->_setPageSize($paperSize, $orientation); $pdf->addPage($orientation); @@ -69,4 +79,21 @@ class MPDF extends AbstractRenderer implements WriterInterface parent::restoreStateAfterSave($fileHandle); } + + /** + * Return classname of MPDF to instantiate + * + * @codeCoverageIgnore + * @return string + */ + private function getMPdfClassName() + { + if ($this->includeFile != null) { + // MPDF version 5.* + return '\mpdf'; + } + + // MPDF version > 6.* + return '\Mpdf\Mpdf'; + } } diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php index e1e19006..badab046 100644 --- a/src/PhpWord/Writer/PDF/TCPDF.php +++ b/src/PhpWord/Writer/PDF/TCPDF.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PhpWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PhpWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Writer\WriterInterface; * * @deprecated 0.13.0 Use `DomPDF` or `MPDF` instead. * - * @link http://www.tcpdf.org/ + * @see http://www.tcpdf.org/ * @since 0.11.0 */ class TCPDF extends AbstractRenderer implements WriterInterface @@ -40,7 +40,6 @@ class TCPDF extends AbstractRenderer implements WriterInterface * Save PhpWord to file. * * @param string $filename Name of the file to save as - * @return vois */ public function save($filename = null) { @@ -55,21 +54,21 @@ class TCPDF extends AbstractRenderer implements WriterInterface $pdf->setFontSubsetting(false); $pdf->setPrintHeader(false); $pdf->setPrintFooter(false); - $pdf->addPage(); - $pdf->setFont($this->getFont()); + $pdf->AddPage(); + $pdf->SetFont($this->getFont()); $pdf->writeHTML($this->getContent()); // Write document properties $phpWord = $this->getPhpWord(); $docProps = $phpWord->getDocInfo(); - $pdf->setTitle($docProps->getTitle()); - $pdf->setAuthor($docProps->getCreator()); - $pdf->setSubject($docProps->getSubject()); - $pdf->setKeywords($docProps->getKeywords()); - $pdf->setCreator($docProps->getCreator()); + $pdf->SetTitle($docProps->getTitle()); + $pdf->SetAuthor($docProps->getCreator()); + $pdf->SetSubject($docProps->getSubject()); + $pdf->SetKeywords($docProps->getKeywords()); + $pdf->SetCreator($docProps->getCreator()); // Write to file - fwrite($fileHandle, $pdf->output($filename, 'S')); + fwrite($fileHandle, $pdf->Output($filename, 'S')); parent::restoreStateAfterSave($fileHandle); } diff --git a/src/PhpWord/Writer/RTF.php b/src/PhpWord/Writer/RTF.php index 887b1c67..0604e8b5 100644 --- a/src/PhpWord/Writer/RTF.php +++ b/src/PhpWord/Writer/RTF.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -58,9 +58,6 @@ class RTF extends AbstractWriter implements WriterInterface * Save content to file. * * @param string $filename - * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\Exception */ public function save($filename = null) @@ -121,7 +118,6 @@ class RTF extends AbstractWriter implements WriterInterface * Set last paragraph style. * * @param mixed $value - * @return void */ public function setLastParagraphStyle($value = '') { diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php index 289733dc..cf1aa391 100644 --- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -59,8 +59,6 @@ abstract class AbstractElement extends HTMLAbstractElement /** * Get font and paragraph styles. - * - * @return void */ protected function getStyles() { @@ -112,6 +110,7 @@ abstract class AbstractElement extends HTMLAbstractElement $styleWriter = new ParagraphStyleWriter($this->paragraphStyle); $styleWriter->setNestedLevel($this->element->getNestedLevel()); + return $styleWriter->write(); } @@ -125,9 +124,9 @@ abstract class AbstractElement extends HTMLAbstractElement { if (Settings::isOutputEscapingEnabled()) { return $this->escaper->escape($text); - } else { - return CommonText::toUnicode($text); // todo: replace with `return $text;` later. } + + return CommonText::toUnicode($text); // todo: replace with `return $text;` later. } /** diff --git a/src/PhpWord/Writer/RTF/Element/Container.php b/src/PhpWord/Writer/RTF/Element/Container.php index 7a1b0b07..58c19256 100644 --- a/src/PhpWord/Writer/RTF/Element/Container.php +++ b/src/PhpWord/Writer/RTF/Element/Container.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/Image.php b/src/PhpWord/Writer/RTF/Element/Image.php index e950d30b..f1e72700 100644 --- a/src/PhpWord/Writer/RTF/Element/Image.php +++ b/src/PhpWord/Writer/RTF/Element/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/Link.php b/src/PhpWord/Writer/RTF/Element/Link.php index f106d57d..25954ed8 100644 --- a/src/PhpWord/Writer/RTF/Element/Link.php +++ b/src/PhpWord/Writer/RTF/Element/Link.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/ListItem.php b/src/PhpWord/Writer/RTF/Element/ListItem.php index b2ba612d..29e7f660 100644 --- a/src/PhpWord/Writer/RTF/Element/ListItem.php +++ b/src/PhpWord/Writer/RTF/Element/ListItem.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/PageBreak.php b/src/PhpWord/Writer/RTF/Element/PageBreak.php index ac2bb8ec..6b08c9cc 100644 --- a/src/PhpWord/Writer/RTF/Element/PageBreak.php +++ b/src/PhpWord/Writer/RTF/Element/PageBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php index c65d7248..8154aa7c 100644 --- a/src/PhpWord/Writer/RTF/Element/Table.php +++ b/src/PhpWord/Writer/RTF/Element/Table.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/Text.php b/src/PhpWord/Writer/RTF/Element/Text.php index b5a28adf..b9e56e89 100644 --- a/src/PhpWord/Writer/RTF/Element/Text.php +++ b/src/PhpWord/Writer/RTF/Element/Text.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -34,7 +34,7 @@ class Text extends AbstractElement /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ $element = $this->element; $elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); - if (!$element instanceof $elementClass) { + if (!$element instanceof $elementClass || !is_string($element->getText())) { return ''; } diff --git a/src/PhpWord/Writer/RTF/Element/TextBreak.php b/src/PhpWord/Writer/RTF/Element/TextBreak.php index 0f76aea2..4aab2767 100644 --- a/src/PhpWord/Writer/RTF/Element/TextBreak.php +++ b/src/PhpWord/Writer/RTF/Element/TextBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/TextRun.php b/src/PhpWord/Writer/RTF/Element/TextRun.php index f63f338d..bfd161f0 100644 --- a/src/PhpWord/Writer/RTF/Element/TextRun.php +++ b/src/PhpWord/Writer/RTF/Element/TextRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index 894f52cc..a9940ca9 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Part/AbstractPart.php b/src/PhpWord/Writer/RTF/Part/AbstractPart.php index 152493db..8171b0d2 100644 --- a/src/PhpWord/Writer/RTF/Part/AbstractPart.php +++ b/src/PhpWord/Writer/RTF/Part/AbstractPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -48,8 +48,6 @@ abstract class AbstractPart /** * @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer - * - * @return void */ public function setParentWriter(AbstractWriter $writer = null) { @@ -57,16 +55,14 @@ abstract class AbstractPart } /** - * @return \PhpOffice\PhpWord\Writer\AbstractWriter - * * @throws \PhpOffice\PhpWord\Exception\Exception + * @return \PhpOffice\PhpWord\Writer\AbstractWriter */ public function getParentWriter() { if ($this->parentWriter !== null) { return $this->parentWriter; - } else { - throw new Exception('No parent WriterInterface assigned.'); } + throw new Exception('No parent WriterInterface assigned.'); } } diff --git a/src/PhpWord/Writer/RTF/Part/Document.php b/src/PhpWord/Writer/RTF/Part/Document.php index 24ee7b0a..d4bfadb4 100644 --- a/src/PhpWord/Writer/RTF/Part/Document.php +++ b/src/PhpWord/Writer/RTF/Part/Document.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\Writer\RTF\Style\Section as SectionStyleWriter; * RTF document part writer * * @since 0.11.0 - * @link http://www.biblioscape.com/rtf15_spec.htm#Heading24 + * @see http://www.biblioscape.com/rtf15_spec.htm#Heading24 */ class Document extends AbstractPart { @@ -54,9 +54,13 @@ class Document extends AbstractPart { $docProps = $this->getParentWriter()->getPhpWord()->getDocInfo(); $properties = array('title', 'subject', 'category', 'keywords', 'comment', - 'author', 'operator', 'creatim', 'revtim', 'company', 'manager'); - $mapping = array('comment' => 'description', 'author' => 'creator', 'operator' => 'lastModifiedBy', - 'creatim' => 'created', 'revtim' => 'modified'); + 'author', 'operator', 'creatim', 'revtim', 'company', 'manager', ); + $mapping = array( + 'comment' => 'description', + 'author' => 'creator', + 'operator' => 'lastModifiedBy', + 'creatim' => 'created', + 'revtim' => 'modified', ); $dateFields = array('creatim', 'revtim'); $content = ''; @@ -86,6 +90,10 @@ class Document extends AbstractPart */ private function writeFormatting() { + $docSettings = $this->getParentWriter()->getPhpWord()->getSettings(); + // Applies a language to a text run (defaults to 1036 : French (France)) + $langId = $docSettings->getThemeFontLang() != null && $docSettings->getThemeFontLang()->getLangId() != null ? $docSettings->getThemeFontLang()->getLangId() : 1036; + $content = ''; $content .= '\deftab720'; // Set the default tab size (720 twips) @@ -94,7 +102,7 @@ class Document extends AbstractPart $content .= '\uc1'; // Set the numberof bytes that follows a unicode character $content .= '\pard'; // Resets to default paragraph properties. $content .= '\nowidctlpar'; // No widow/orphan control - $content .= '\lang1036'; // Applies a language to a text run (1036 : French (France)) + $content .= '\lang' . $langId; $content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs $content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points $content .= PHP_EOL; diff --git a/src/PhpWord/Writer/RTF/Part/Header.php b/src/PhpWord/Writer/RTF/Part/Header.php index 56a50349..01439bc6 100644 --- a/src/PhpWord/Writer/RTF/Part/Header.php +++ b/src/PhpWord/Writer/RTF/Part/Header.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,7 +33,7 @@ use PhpOffice\PhpWord\Style\Font; * - List table (not supported yet) * * @since 0.11.0 - * @link http://www.biblioscape.com/rtf15_spec.htm#Heading6 + * @see http://www.biblioscape.com/rtf15_spec.htm#Heading6 */ class Header extends AbstractPart { @@ -181,8 +181,6 @@ class Header extends AbstractPart /** * Register all fonts and colors in both named and inline styles to appropriate header table. - * - * @return void */ private function registerFont() { @@ -213,7 +211,6 @@ class Header extends AbstractPart * Register border colors. * * @param \PhpOffice\PhpWord\Style\Border $style - * @return void */ private function registerBorderColor($style) { @@ -229,7 +226,6 @@ class Header extends AbstractPart * Register fonts and colors. * * @param \PhpOffice\PhpWord\Style\AbstractStyle $style - * @return void */ private function registerFontItems($style) { @@ -249,7 +245,6 @@ class Header extends AbstractPart * @param array &$table * @param string $value * @param string $default - * @return void */ private function registerTableItem(&$table, $value, $default = null) { diff --git a/src/PhpWord/Writer/RTF/Style/AbstractStyle.php b/src/PhpWord/Writer/RTF/Style/AbstractStyle.php index 417be9cf..57aa6bb9 100644 --- a/src/PhpWord/Writer/RTF/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/RTF/Style/AbstractStyle.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/RTF/Style/Border.php b/src/PhpWord/Writer/RTF/Style/Border.php index 9f7ee2c0..08dcf018 100644 --- a/src/PhpWord/Writer/RTF/Style/Border.php +++ b/src/PhpWord/Writer/RTF/Style/Border.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -48,7 +48,7 @@ class Border extends AbstractStyle $content = ''; $sides = array('top', 'left', 'right', 'bottom'); - $sizeCount = count($this->sizes) - 1; + $sizeCount = count($this->sizes); // Page border measure // 8 = from text, infront off; 32 = from edge, infront on; 40 = from edge, infront off @@ -103,8 +103,7 @@ class Border extends AbstractStyle /** * Set sizes. * - * @param integer[] $value - * @return void + * @param int[] $value */ public function setSizes($value) { @@ -115,7 +114,6 @@ class Border extends AbstractStyle * Set colors. * * @param string[] $value - * @return void */ public function setColors($value) { diff --git a/src/PhpWord/Writer/RTF/Style/Font.php b/src/PhpWord/Writer/RTF/Style/Font.php index 6567ec33..8c729425 100644 --- a/src/PhpWord/Writer/RTF/Style/Font.php +++ b/src/PhpWord/Writer/RTF/Style/Font.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -62,7 +62,7 @@ class Font extends AbstractStyle $content .= $this->getValueIf($style->isSuperScript(), '\super'); $content .= $this->getValueIf($style->isSubScript(), '\sub'); - return $content . ' '; + return $content . ' '; } /** @@ -70,7 +70,6 @@ class Font extends AbstractStyle * * * @param int $value - * @return void */ public function setNameIndex($value = 0) { @@ -81,7 +80,6 @@ class Font extends AbstractStyle * Set font color index. * * @param int $value - * @return void */ public function setColorIndex($value = 0) { diff --git a/src/PhpWord/Writer/RTF/Style/Indentation.php b/src/PhpWord/Writer/RTF/Style/Indentation.php new file mode 100644 index 00000000..dd52230e --- /dev/null +++ b/src/PhpWord/Writer/RTF/Style/Indentation.php @@ -0,0 +1,45 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Indentation) { + return ''; + } + + $content = '\fi' . $style->getFirstLine(); + $content .= '\li' . $style->getLeft(); + $content .= '\ri' . $style->getRight(); + + return $content . ' '; + } +} diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 046adc8c..cb50a31b 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -64,9 +64,49 @@ class Paragraph extends AbstractStyle if (isset($alignments[$style->getAlignment()])) { $content .= $alignments[$style->getAlignment()]; } + $content .= $this->writeIndentation($style->getIndentation()); $content .= $this->getValueIf($spaceBefore !== null, '\sb' . $spaceBefore); $content .= $this->getValueIf($spaceAfter !== null, '\sa' . $spaceAfter); + $styles = $style->getStyleValues(); + $content .= $this->writeTabs($styles['tabs']); + + return $content; + } + + /** + * Writes an \PhpOffice\PhpWord\Style\Indentation + * + * @param null|\PhpOffice\PhpWord\Style\Indentation $indent + * @return string + */ + private function writeIndentation($indent = null) + { + if (isset($indent) && $indent instanceof \PhpOffice\PhpWord\Style\Indentation) { + $writer = new Indentation($indent); + + return $writer->write(); + } + + return ''; + } + + /** + * Writes tabs + * + * @param \PhpOffice\PhpWord\Style\Tab[] $tabs + * @return string + */ + private function writeTabs($tabs = null) + { + $content = ''; + if (!empty($tabs)) { + foreach ($tabs as $tab) { + $styleWriter = new Tab($tab); + $content .= $styleWriter->write(); + } + } + return $content; } @@ -74,7 +114,6 @@ class Paragraph extends AbstractStyle * Set nested level. * * @param int $value - * @return void */ public function setNestedLevel($value) { diff --git a/src/PhpWord/Writer/RTF/Style/Section.php b/src/PhpWord/Writer/RTF/Style/Section.php index dcdc0aaf..ee6efcf3 100644 --- a/src/PhpWord/Writer/RTF/Style/Section.php +++ b/src/PhpWord/Writer/RTF/Style/Section.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -43,8 +43,8 @@ class Section extends AbstractStyle $content .= '\sectd '; // Size & margin - $content .= $this->getValueIf($style->getPageSizeW() !== null, '\pgwsxn' . $style->getPageSizeW()); - $content .= $this->getValueIf($style->getPageSizeH() !== null, '\pghsxn' . $style->getPageSizeH()); + $content .= $this->getValueIf($style->getPageSizeW() !== null, '\pgwsxn' . round($style->getPageSizeW())); + $content .= $this->getValueIf($style->getPageSizeH() !== null, '\pghsxn' . round($style->getPageSizeH())); $content .= ' '; $content .= $this->getValueIf($style->getMarginTop() !== null, '\margtsxn' . $style->getMarginTop()); $content .= $this->getValueIf($style->getMarginRight() !== null, '\margrsxn' . $style->getMarginRight()); diff --git a/src/PhpWord/Writer/RTF/Style/Tab.php b/src/PhpWord/Writer/RTF/Style/Tab.php new file mode 100644 index 00000000..fe1f9363 --- /dev/null +++ b/src/PhpWord/Writer/RTF/Style/Tab.php @@ -0,0 +1,49 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Tab) { + return; + } + $tabs = array( + \PhpOffice\PhpWord\Style\Tab::TAB_STOP_RIGHT => '\tqr', + \PhpOffice\PhpWord\Style\Tab::TAB_STOP_CENTER => '\tqc', + \PhpOffice\PhpWord\Style\Tab::TAB_STOP_DECIMAL => '\tqdec', + ); + $content = ''; + if (isset($tabs[$style->getType()])) { + $content .= $tabs[$style->getType()]; + } + $content .= '\tx' . $style->getPosition(); + + return $content; + } +} diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 8e10f5f6..eee215be 100644 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -60,6 +60,7 @@ class Word2007 extends AbstractWriter implements WriterInterface 'DocPropsCustom' => 'docProps/custom.xml', 'RelsDocument' => 'word/_rels/document.xml.rels', 'Document' => 'word/document.xml', + 'Comments' => 'word/comments.xml', 'Styles' => 'word/styles.xml', 'Numbering' => 'word/numbering.xml', 'Settings' => 'word/settings.xml', @@ -91,7 +92,6 @@ class Word2007 extends AbstractWriter implements WriterInterface * Save document by name. * * @param string $filename - * @return void */ public function save($filename = null) { @@ -129,6 +129,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->addNotes($zip, $rId, 'footnote'); $this->addNotes($zip, $rId, 'endnote'); + $this->addComments($zip, $rId); $this->addChart($zip, $rId); // Write parts @@ -168,7 +169,6 @@ class Word2007 extends AbstractWriter implements WriterInterface * * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip * @param string $docPart - * @return void */ private function addHeaderFooterMedia(ZipArchive $zip, $docPart) { @@ -195,16 +195,15 @@ class Word2007 extends AbstractWriter implements WriterInterface * @param \PhpOffice\PhpWord\Element\Section &$section * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip * @param string $elmType header|footer - * @param integer &$rId - * @return void + * @param int &$rId */ private function addHeaderFooterContent(Section &$section, ZipArchive $zip, $elmType, &$rId) { $getFunction = $elmType == 'header' ? 'getHeaders' : 'getFooters'; $elmCount = ($section->getSectionId() - 1) * 3; $elements = $section->$getFunction(); + /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */ foreach ($elements as &$element) { - /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */ $elmCount++; $element->setRelationId(++$rId); $elmFile = "{$elmType}{$elmCount}.xml"; // e.g. footer1.xml @@ -221,9 +220,8 @@ class Word2007 extends AbstractWriter implements WriterInterface * Add footnotes/endnotes * * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip - * @param integer &$rId + * @param int &$rId * @param string $noteType - * @return void */ private function addNotes(ZipArchive $zip, &$rId, $noteType = 'footnote') { @@ -255,12 +253,34 @@ class Word2007 extends AbstractWriter implements WriterInterface } } + /** + * Add comments + * + * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip + * @param int &$rId + */ + private function addComments(ZipArchive $zip, &$rId) + { + $phpWord = $this->getPhpWord(); + $collection = $phpWord->getComments(); + $partName = 'comments'; + + // Add comment relations and contents + /** @var \PhpOffice\PhpWord\Collection\AbstractCollection $collection Type hint */ + if ($collection->countItems() > 0) { + $this->relationships[] = array('target' => "{$partName}.xml", 'type' => $partName, 'rID' => ++$rId); + + // Write content file, e.g. word/comments.xml + $writerPart = $this->getWriterPart($partName)->setElements($collection->getItems()); + $zip->addFromString("word/{$partName}.xml", $writerPart->write()); + } + } + /** * Add chart. * * @param \PhpOffice\PhpWord\Shared\ZipArchive $zip - * @param integer &$rId - * @return void + * @param int &$rId */ private function addChart(ZipArchive $zip, &$rId) { @@ -269,6 +289,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $collection = $phpWord->getCharts(); $index = 0; if ($collection->countItems() > 0) { + /** @var \PhpOffice\PhpWord\Element\Chart $chart */ foreach ($collection->getItems() as $chart) { $index++; $rId++; @@ -281,7 +302,6 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->relationships[] = array('target' => $filename, 'type' => 'chart', 'rID' => $rId); // word/charts/chartN.xml - /** @var \PhpOffice\PhpWord\Element\Chart $chart */ $chart->setRelationId($rId); $writerPart = $this->getWriterPart('Chart'); $writerPart->setElement($chart); @@ -294,7 +314,6 @@ class Word2007 extends AbstractWriter implements WriterInterface * Register content types for each media. * * @param array $media - * @return void */ private function registerContentTypes($media) { diff --git a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php index f5a454d2..63f45a76 100644 --- a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php +++ b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,6 +20,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; use PhpOffice\Common\Text as CommonText; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\AbstractElement as Element; +use PhpOffice\PhpWord\Settings; /** * Abstract element writer @@ -92,7 +93,6 @@ abstract class AbstractElement * Start w:p DOM element. * * @uses \PhpOffice\PhpWord\Writer\Word2007\Element\PageBreak::write() - * @return void */ protected function startElementP() { @@ -103,24 +103,68 @@ abstract class AbstractElement $this->writeParagraphStyle(); } } + $this->writeCommentRangeStart(); } /** * End w:p DOM element. - * - * @return void */ protected function endElementP() { + $this->writeCommentRangeEnd(); if (!$this->withoutP) { $this->xmlWriter->endElement(); // w:p } } + /** + * Writes the w:commentRangeStart DOM element + */ + protected function writeCommentRangeStart() + { + if ($this->element->getCommentRangeStart() != null) { + $comment = $this->element->getCommentRangeStart(); + //only set the ID if it is not yet set, otherwise it will overwrite it + if ($comment->getElementId() == null) { + $comment->setElementId(); + } + + $this->xmlWriter->writeElementBlock('w:commentRangeStart', array('w:id' => $comment->getElementId())); + } + } + + /** + * Writes the w:commentRangeEnd DOM element + */ + protected function writeCommentRangeEnd() + { + if ($this->element->getCommentRangeEnd() != null) { + $comment = $this->element->getCommentRangeEnd(); + //only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen + if ($comment->getElementId() == null) { + $comment->setElementId(); // @codeCoverageIgnore + } // @codeCoverageIgnore + + $this->xmlWriter->writeElementBlock('w:commentRangeEnd', array('w:id' => $comment->getElementId())); + $this->xmlWriter->startElement('w:r'); + $this->xmlWriter->writeElementBlock('w:commentReference', array('w:id' => $comment->getElementId())); + $this->xmlWriter->endElement(); + } elseif ($this->element->getCommentRangeStart() != null && $this->element->getCommentRangeStart()->getEndElement() == null) { + $comment = $this->element->getCommentRangeStart(); + //only set the ID if it is not yet set, otherwise it will overwrite it, this should normally not happen + if ($comment->getElementId() == null) { + $comment->setElementId(); // @codeCoverageIgnore + } // @codeCoverageIgnore + + $this->xmlWriter->writeElementBlock('w:commentRangeEnd', array('w:id' => $comment->getElementId())); + $this->xmlWriter->startElement('w:r'); + $this->xmlWriter->writeElementBlock('w:commentReference', array('w:id' => $comment->getElementId())); + $this->xmlWriter->endElement(); + } + } + /** * Write ending. - * - * @return void */ protected function writeParagraphStyle() { @@ -129,20 +173,16 @@ abstract class AbstractElement /** * Write ending. - * - * @return void */ protected function writeFontStyle() { $this->writeTextStyle('Font'); } - /** * Write text style. * * @param string $styleType Font|Paragraph - * @return void */ private function writeTextStyle($styleType) { @@ -150,12 +190,12 @@ abstract class AbstractElement $class = "PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\{$styleType}"; $styleObject = $this->element->$method(); + /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $styleWriter Type Hint */ $styleWriter = new $class($this->xmlWriter, $styleObject); if (method_exists($styleWriter, 'setIsInline')) { $styleWriter->setIsInline(true); } - /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $styleWriter */ $styleWriter->write(); } @@ -169,4 +209,19 @@ abstract class AbstractElement { return CommonText::controlCharacterPHP2OOXML($text); } + + /** + * Write an XML text, this will call text() or writeRaw() depending on the value of Settings::isOutputEscapingEnabled() + * + * @param string $content The text string to write + * @return bool Returns true on success or false on failure + */ + protected function writeText($content) + { + if (Settings::isOutputEscapingEnabled()) { + return $this->getXmlWriter()->text($content); + } + + return $this->getXmlWriter()->writeRaw($content); + } } diff --git a/src/PhpWord/Writer/Word2007/Element/Bookmark.php b/src/PhpWord/Writer/Word2007/Element/Bookmark.php index 424fb0ab..04eaacf3 100644 --- a/src/PhpWord/Writer/Word2007/Element/Bookmark.php +++ b/src/PhpWord/Writer/Word2007/Element/Bookmark.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -41,7 +41,7 @@ class Bookmark extends AbstractElement $xmlWriter->writeAttribute('w:id', $rId); $xmlWriter->writeAttribute('w:name', $element->getName()); $xmlWriter->endElement(); - + $xmlWriter->startElement('w:bookmarkEnd'); $xmlWriter->writeAttribute('w:id', $rId); $xmlWriter->endElement(); diff --git a/src/PhpWord/Writer/Word2007/Element/Chart.php b/src/PhpWord/Writer/Word2007/Element/Chart.php index 12602532..f88ca2d2 100644 --- a/src/PhpWord/Writer/Word2007/Element/Chart.php +++ b/src/PhpWord/Writer/Word2007/Element/Chart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class Chart extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { @@ -45,6 +43,7 @@ class Chart extends AbstractElement if (!$this->withoutP) { $xmlWriter->startElement('w:p'); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:drawing'); diff --git a/src/PhpWord/Writer/Word2007/Element/CheckBox.php b/src/PhpWord/Writer/Word2007/Element/CheckBox.php index 7424985c..05692a07 100644 --- a/src/PhpWord/Writer/Word2007/Element/CheckBox.php +++ b/src/PhpWord/Writer/Word2007/Element/CheckBox.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Settings; - /** * CheckBox element writer * @@ -28,8 +26,6 @@ class CheckBox extends Text { /** * Write element. - * - * @return void */ public function write() { @@ -66,17 +62,17 @@ class CheckBox extends Text $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); $xmlWriter->text(' FORMCHECKBOX '); - $xmlWriter->endElement();// w:instrText + $xmlWriter->endElement(); // w:instrText $xmlWriter->endElement(); // w:r $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:fldChar'); $xmlWriter->writeAttribute('w:fldCharType', 'separate'); - $xmlWriter->endElement();// w:fldChar + $xmlWriter->endElement(); // w:fldChar $xmlWriter->endElement(); // w:r $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:fldChar'); $xmlWriter->writeAttribute('w:fldCharType', 'end'); - $xmlWriter->endElement();// w:fldChar + $xmlWriter->endElement(); // w:fldChar $xmlWriter->endElement(); // w:r $xmlWriter->startElement('w:r'); @@ -85,11 +81,7 @@ class CheckBox extends Text $xmlWriter->startElement('w:t'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($this->getText($element->getText())); - } else { - $xmlWriter->writeRaw($this->getText($element->getText())); - } + $this->writeText($this->getText($element->getText())); $xmlWriter->endElement(); // w:t $xmlWriter->endElement(); // w:r diff --git a/src/PhpWord/Writer/Word2007/Element/Container.php b/src/PhpWord/Writer/Word2007/Element/Container.php index 0efd0ebc..892da051 100644 --- a/src/PhpWord/Writer/Word2007/Element/Container.php +++ b/src/PhpWord/Writer/Word2007/Element/Container.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -38,8 +38,6 @@ class Container extends AbstractElement /** * Write element. - * - * @return void */ public function write() { @@ -48,7 +46,7 @@ class Container extends AbstractElement return; } $containerClass = substr(get_class($container), strrpos(get_class($container), '\\') + 1); - $withoutP = in_array($containerClass, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun')) ? true : false; + $withoutP = in_array($containerClass, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun')); $xmlWriter = $this->getXmlWriter(); // Loop through elements diff --git a/src/PhpWord/Writer/Word2007/Element/Endnote.php b/src/PhpWord/Writer/Word2007/Element/Endnote.php index 9363489e..9a2eb3ff 100644 --- a/src/PhpWord/Writer/Word2007/Element/Endnote.php +++ b/src/PhpWord/Writer/Word2007/Element/Endnote.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Element/Field.php b/src/PhpWord/Writer/Word2007/Element/Field.php index ae4c66ba..e79dd24a 100644 --- a/src/PhpWord/Writer/Word2007/Element/Field.php +++ b/src/PhpWord/Writer/Word2007/Element/Field.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,27 +26,159 @@ class Field extends Text { /** * Write field element. - * - * @return void */ public function write() { - $xmlWriter = $this->getXmlWriter(); - $element = $this->getElement(); + $element = $this->getElement(); if (!$element instanceof \PhpOffice\PhpWord\Element\Field) { return; } + $methodName = 'write' . ucfirst(strtolower($element->getType())); + if (method_exists($this, $methodName)) { + $this->$methodName($element); + } else { + $this->writeDefault($element); + } + } + + private function writeDefault(\PhpOffice\PhpWord\Element\Field $element) + { + $xmlWriter = $this->getXmlWriter(); + $this->startElementP(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'begin'); + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + $instruction = ' ' . $element->getType() . ' '; - $properties = $element->getProperties(); + if ($element->getText() != null) { + if (is_string($element->getText())) { + $instruction .= '"' . $element->getText() . '" '; + $instruction .= $this->buildPropertiesAndOptions($element); + } else { + $instruction .= '"'; + } + } else { + $instruction .= $this->buildPropertiesAndOptions($element); + } + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->text($instruction); + $xmlWriter->endElement(); // w:instrText + $xmlWriter->endElement(); // w:r + + if ($element->getText() != null) { + if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) { + $containerWriter = new Container($xmlWriter, $element->getText(), true); + $containerWriter->write(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->text('"' . $this->buildPropertiesAndOptions($element)); + $xmlWriter->endElement(); // w:instrText + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->text(' '); + $xmlWriter->endElement(); // w:instrText + $xmlWriter->endElement(); // w:r + } + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'separate'); + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:rPr'); + $xmlWriter->startElement('w:noProof'); + $xmlWriter->endElement(); // w:noProof + $xmlWriter->endElement(); // w:rPr + $xmlWriter->writeElement('w:t', $element->getText() != null && is_string($element->getText()) ? $element->getText() : '1'); + $xmlWriter->endElement(); // w:r + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'end'); + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $this->endElementP(); // w:p + } + + /** + * Writes a macrobutton field + * + * //TODO A lot of code duplication with general method, should maybe be refactored + * @param \PhpOffice\PhpWord\Element\Field $element + */ + protected function writeMacrobutton(\PhpOffice\PhpWord\Element\Field $element) + { + $xmlWriter = $this->getXmlWriter(); + $this->startElementP(); + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'begin'); + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $instruction = ' ' . $element->getType() . ' ' . $this->buildPropertiesAndOptions($element); + if (is_string($element->getText())) { + $instruction .= $element->getText() . ' '; + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:instrText'); + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $xmlWriter->text($instruction); + $xmlWriter->endElement(); // w:instrText + $xmlWriter->endElement(); // w:r + + if ($element->getText() != null) { + if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) { + $containerWriter = new Container($xmlWriter, $element->getText(), true); + $containerWriter->write(); + } + } + + $xmlWriter->startElement('w:r'); + $xmlWriter->startElement('w:fldChar'); + $xmlWriter->writeAttribute('w:fldCharType', 'end'); + $xmlWriter->endElement(); // w:fldChar + $xmlWriter->endElement(); // w:r + + $this->endElementP(); // w:p + } + + private function buildPropertiesAndOptions(\PhpOffice\PhpWord\Element\Field $element) + { + $propertiesAndOptions = ''; + $properties = $element->getProperties(); foreach ($properties as $propkey => $propval) { switch ($propkey) { case 'format': + $propertiesAndOptions .= '\* ' . $propval . ' '; + break; case 'numformat': - $instruction .= '\* ' . $propval . ' '; + $propertiesAndOptions .= '\# ' . $propval . ' '; break; case 'dateformat': - $instruction .= '\@ "' . $propval . '" '; + $propertiesAndOptions .= '\@ "' . $propval . '" '; + break; + case 'macroname': + $propertiesAndOptions .= $propval . ' '; + break; + default: + $propertiesAndOptions .= '"' . $propval . '" '; break; } } @@ -55,34 +187,28 @@ class Field extends Text foreach ($options as $option) { switch ($option) { case 'PreserveFormat': - $instruction .= '\* MERGEFORMAT '; + $propertiesAndOptions .= '\* MERGEFORMAT '; break; case 'LunarCalendar': - $instruction .= '\h '; + $propertiesAndOptions .= '\h '; break; case 'SakaEraCalendar': - $instruction .= '\s '; + $propertiesAndOptions .= '\s '; break; case 'LastUsedFormat': - $instruction .= '\l '; + $propertiesAndOptions .= '\l '; break; + case 'Bold': + $propertiesAndOptions .= '\b '; + break; + case 'Italic': + $propertiesAndOptions .= '\i '; + break; + default: + $propertiesAndOptions .= $option . ' '; } } - $this->startElementP(); - - $xmlWriter->startElement('w:fldSimple'); - $xmlWriter->writeAttribute('w:instr', $instruction); - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:noProof'); - $xmlWriter->endElement(); // w:noProof - $xmlWriter->endElement(); // w:rPr - - $xmlWriter->writeElement('w:t', '1'); - $xmlWriter->endElement(); // w:r - $xmlWriter->endElement(); // w:fldSimple - - $this->endElementP(); // w:p + return $propertiesAndOptions; } } diff --git a/src/PhpWord/Writer/Word2007/Element/Footnote.php b/src/PhpWord/Writer/Word2007/Element/Footnote.php index 53fcd6a0..56a5332f 100644 --- a/src/PhpWord/Writer/Word2007/Element/Footnote.php +++ b/src/PhpWord/Writer/Word2007/Element/Footnote.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,8 +33,6 @@ class Footnote extends Text /** * Write element. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/FormField.php b/src/PhpWord/Writer/Word2007/Element/FormField.php index 27df756f..b59cf58f 100644 --- a/src/PhpWord/Writer/Word2007/Element/FormField.php +++ b/src/PhpWord/Writer/Word2007/Element/FormField.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,6 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\FormField as FormFieldElement; -use PhpOffice\PhpWord\Settings; /** * FormField element writer @@ -27,7 +26,7 @@ use PhpOffice\PhpWord\Settings; * Note: DropDown is active when document protection is set to `forms` * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFData.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFData.html * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ class FormField extends Text @@ -37,8 +36,6 @@ class FormField extends Text /** * Write element. - * - * @return void */ public function write() { @@ -80,7 +77,7 @@ class FormField extends Text $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); $xmlWriter->text("{$instruction}"); - $xmlWriter->endElement();// w:instrText + $xmlWriter->endElement(); // w:instrText $xmlWriter->endElement(); // w:r $xmlWriter->startElement('w:r'); @@ -92,11 +89,7 @@ class FormField extends Text $this->writeFontStyle(); $xmlWriter->startElement('w:t'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($value); - } else { - $xmlWriter->writeRaw($value); - } + $this->writeText($value); $xmlWriter->endElement(); // w:t $xmlWriter->endElement(); // w:r @@ -111,10 +104,9 @@ class FormField extends Text /** * Write textinput. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFTextInput.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFTextInput.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element - * @return void */ private function writeTextInput(XMLWriter $xmlWriter, FormFieldElement $element) { @@ -128,10 +120,9 @@ class FormField extends Text /** * Write checkbox. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFCheckBox.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFCheckBox.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element - * @return void */ private function writeCheckBox(XMLWriter $xmlWriter, FormFieldElement $element) { @@ -152,10 +143,9 @@ class FormField extends Text /** * Write dropdown. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_FFDDList.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFDDList.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element - * @return void */ private function writeDropDown(XMLWriter $xmlWriter, FormFieldElement $element) { @@ -170,6 +160,9 @@ class FormField extends Text $xmlWriter->writeElementBlock('w:result', 'w:val', $value); $xmlWriter->writeElementBlock('w:default', 'w:val', $default); foreach ($entries as $entry) { + if ($entry == null || $entry == '') { + $entry = str_repeat(' ', self::FILLER_LENGTH); + } $xmlWriter->writeElementBlock('w:listEntry', 'w:val', $entry); } $xmlWriter->endElement(); diff --git a/src/PhpWord/Writer/Word2007/Element/Image.php b/src/PhpWord/Writer/Word2007/Element/Image.php index 914c78ea..5bebb89c 100644 --- a/src/PhpWord/Writer/Word2007/Element/Image.php +++ b/src/PhpWord/Writer/Word2007/Element/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,6 +19,9 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\Image as ImageElement; +use PhpOffice\PhpWord\Style\Font as FontStyle; +use PhpOffice\PhpWord\Style\Frame as FrameStyle; +use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter; use PhpOffice\PhpWord\Writer\Word2007\Style\Image as ImageStyleWriter; /** @@ -30,8 +33,6 @@ class Image extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { @@ -50,8 +51,6 @@ class Image extends AbstractElement /** * Write image element. - * - * @return void */ private function writeImage(XMLWriter $xmlWriter, ImageElement $element) { @@ -63,8 +62,19 @@ class Image extends AbstractElement $xmlWriter->startElement('w:p'); $styleWriter->writeAlignment(); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); + + // Write position + $position = $style->getPosition(); + if ($position && $style->getWrap() == FrameStyle::WRAP_INLINE) { + $fontStyle = new FontStyle('text'); + $fontStyle->setPosition($position); + $fontStyleWriter = new FontStyleWriter($xmlWriter, $fontStyle); + $fontStyleWriter->write(); + } + $xmlWriter->startElement('w:pict'); $xmlWriter->startElement('v:shape'); $xmlWriter->writeAttribute('type', '#_x0000_t75'); @@ -85,8 +95,6 @@ class Image extends AbstractElement /** * Write watermark element. - * - * @return void */ private function writeWatermark(XMLWriter $xmlWriter, ImageElement $element) { @@ -95,7 +103,9 @@ class Image extends AbstractElement $style->setPositioning('absolute'); $styleWriter = new ImageStyleWriter($xmlWriter, $style); - $xmlWriter->startElement('w:p'); + if (!$this->withoutP) { + $xmlWriter->startElement('w:p'); + } $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:pict'); $xmlWriter->startElement('v:shape'); @@ -110,6 +120,8 @@ class Image extends AbstractElement $xmlWriter->endElement(); // v:shape $xmlWriter->endElement(); // w:pict $xmlWriter->endElement(); // w:r - $xmlWriter->endElement(); // w:p + if (!$this->withoutP) { + $xmlWriter->endElement(); // w:p + } } } diff --git a/src/PhpWord/Writer/Word2007/Element/Line.php b/src/PhpWord/Writer/Word2007/Element/Line.php index ade91fb8..a114be60 100644 --- a/src/PhpWord/Writer/Word2007/Element/Line.php +++ b/src/PhpWord/Writer/Word2007/Element/Line.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,19 +22,16 @@ use PhpOffice\PhpWord\Writer\Word2007\Style\Line as LineStyleWriter; /** * Line element writer - * */ class Line extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { $xmlWriter = $this->getXmlWriter(); - $element = $this->getElement(); + $element = $this->getElement(); if (!$element instanceof LineElement) { return; } @@ -48,6 +45,7 @@ class Line extends AbstractElement $xmlWriter->startElement('w:p'); $styleWriter->writeAlignment(); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:pict'); diff --git a/src/PhpWord/Writer/Word2007/Element/Link.php b/src/PhpWord/Writer/Word2007/Element/Link.php index 5f7ad278..f0adf851 100644 --- a/src/PhpWord/Writer/Word2007/Element/Link.php +++ b/src/PhpWord/Writer/Word2007/Element/Link.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Settings; - /** * Link element writer * @@ -28,8 +26,6 @@ class Link extends Text { /** * Write link element. - * - * @return void */ public function write() { @@ -56,11 +52,7 @@ class Link extends Text $xmlWriter->startElement('w:t'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($element->getText()); - } else { - $xmlWriter->writeRaw($element->getText()); - } + $this->writeText($element->getText()); $xmlWriter->endElement(); // w:t $xmlWriter->endElement(); // w:r $xmlWriter->endElement(); // w:hyperlink diff --git a/src/PhpWord/Writer/Word2007/Element/ListItem.php b/src/PhpWord/Writer/Word2007/Element/ListItem.php index 53644ffa..ef738e10 100644 --- a/src/PhpWord/Writer/Word2007/Element/ListItem.php +++ b/src/PhpWord/Writer/Word2007/Element/ListItem.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class ListItem extends AbstractElement { /** * Write list item element. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/ListItemRun.php b/src/PhpWord/Writer/Word2007/Element/ListItemRun.php index 1ac17a98..765d2ee0 100644 --- a/src/PhpWord/Writer/Word2007/Element/ListItemRun.php +++ b/src/PhpWord/Writer/Word2007/Element/ListItemRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class ListItemRun extends AbstractElement { /** * Write list item element. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/Object.php b/src/PhpWord/Writer/Word2007/Element/OLEObject.php similarity index 90% rename from src/PhpWord/Writer/Word2007/Element/Object.php rename to src/PhpWord/Writer/Word2007/Element/OLEObject.php index 4fdf6fed..c55ff6cd 100644 --- a/src/PhpWord/Writer/Word2007/Element/Object.php +++ b/src/PhpWord/Writer/Word2007/Element/OLEObject.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,22 +20,20 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; use PhpOffice\PhpWord\Writer\Word2007\Style\Image as ImageStyleWriter; /** - * Object element writer + * OLEObject element writer * * @since 0.10.0 */ -class Object extends AbstractElement +class OLEObject extends AbstractElement { /** * Write object element. - * - * @return void */ public function write() { $xmlWriter = $this->getXmlWriter(); $element = $this->getElement(); - if (!$element instanceof \PhpOffice\PhpWord\Element\Object) { + if (!$element instanceof \PhpOffice\PhpWord\Element\OLEObject) { return; } @@ -51,6 +49,7 @@ class Object extends AbstractElement $xmlWriter->startElement('w:p'); $styleWriter->writeAlignment(); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:object'); diff --git a/src/PhpWord/Writer/Word2007/Element/PageBreak.php b/src/PhpWord/Writer/Word2007/Element/PageBreak.php index 363a8c2c..0801e4d3 100644 --- a/src/PhpWord/Writer/Word2007/Element/PageBreak.php +++ b/src/PhpWord/Writer/Word2007/Element/PageBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,7 +28,6 @@ class PageBreak extends AbstractElement * Write element. * * @usedby \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement::startElementP() - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/ParagraphAlignment.php b/src/PhpWord/Writer/Word2007/Element/ParagraphAlignment.php index 2c775d14..5563f607 100644 --- a/src/PhpWord/Writer/Word2007/Element/ParagraphAlignment.php +++ b/src/PhpWord/Writer/Word2007/Element/ParagraphAlignment.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,7 +29,7 @@ class ParagraphAlignment /** * @since 0.13.0 * - * @param string $value Any value provided by Jc simple type. + * @param string $value Any value provided by Jc simple type * * @see \PhpOffice\PhpWord\SimpleType\Jc For the allowed values of $value parameter. */ diff --git a/src/PhpWord/Writer/Word2007/Element/PreserveText.php b/src/PhpWord/Writer/Word2007/Element/PreserveText.php index 82c6f87b..94ce6ede 100644 --- a/src/PhpWord/Writer/Word2007/Element/PreserveText.php +++ b/src/PhpWord/Writer/Word2007/Element/PreserveText.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Settings; - /** * PreserveText element writer * @@ -28,8 +26,6 @@ class PreserveText extends Text { /** * Write preserve text element. - * - * @return void */ public function write() { @@ -62,11 +58,7 @@ class PreserveText extends Text $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($text); - } else { - $xmlWriter->writeRaw($text); - } + $this->writeText($text); $xmlWriter->endElement(); $xmlWriter->endElement(); @@ -88,11 +80,7 @@ class PreserveText extends Text $xmlWriter->startElement('w:t'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($this->getText($text)); - } else { - $xmlWriter->writeRaw($this->getText($text)); - } + $this->writeText($this->getText($text)); $xmlWriter->endElement(); $xmlWriter->endElement(); } diff --git a/src/PhpWord/Writer/Word2007/Element/SDT.php b/src/PhpWord/Writer/Word2007/Element/SDT.php index 313bf7e0..21020a0f 100644 --- a/src/PhpWord/Writer/Word2007/Element/SDT.php +++ b/src/PhpWord/Writer/Word2007/Element/SDT.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,15 +24,13 @@ use PhpOffice\PhpWord\Element\SDT as SDTElement; * Structured document tag element writer * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/t-w_CT_SdtBlock.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtBlock.html * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ class SDT extends Text { /** * Write element. - * - * @return void */ public function write() { @@ -43,6 +41,12 @@ class SDT extends Text } $type = $element->getType(); $writeFormField = "write{$type}"; + $alias = $element->getAlias(); + $tag = $element->getTag(); + $value = $element->getValue(); + if ($value === null) { + $value = 'Pick value'; + } $this->startElementP(); @@ -50,15 +54,17 @@ class SDT extends Text // Properties $xmlWriter->startElement('w:sdtPr'); - $xmlWriter->writeElementBlock('w:id', 'w:val', rand(100000000, 999999999)); + $xmlWriter->writeElementIf($alias != null, 'w:alias', 'w:val', $alias); $xmlWriter->writeElementBlock('w:lock', 'w:val', 'sdtLocked'); + $xmlWriter->writeElementBlock('w:id', 'w:val', rand(100000000, 999999999)); + $xmlWriter->writeElementIf($tag != null, 'w:tag', 'w:val', $tag); $this->$writeFormField($xmlWriter, $element); $xmlWriter->endElement(); // w:sdtPr // Content $xmlWriter->startElement('w:sdtContent'); $xmlWriter->startElement('w:r'); - $xmlWriter->writeElement('w:t', 'Pick value'); + $xmlWriter->writeElement('w:t', $value); $xmlWriter->endElement(); // w:r $xmlWriter->endElement(); // w:sdtContent @@ -70,10 +76,9 @@ class SDT extends Text /** * Write combo box. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_SdtComboBox.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtComboBox.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element - * @return void */ private function writeComboBox(XMLWriter $xmlWriter, SDTElement $element) { @@ -90,23 +95,21 @@ class SDT extends Text /** * Write drop down list. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_SdtDropDownList.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtDropDownList.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element - * @return void */ private function writeDropDownList(XMLWriter $xmlWriter, SDTElement $element) { - $this->writecomboBox($xmlWriter, $element); + $this->writeComboBox($xmlWriter, $element); } /** * Write date. * - * @link http://www.datypic.com/sc/ooxml/t-w_CT_SdtDate.html + * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtDate.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element - * @return void */ private function writeDate(XMLWriter $xmlWriter, SDTElement $element) { diff --git a/src/PhpWord/Writer/Word2007/Element/Shape.php b/src/PhpWord/Writer/Word2007/Element/Shape.php index f282c4a5..250d5c1d 100644 --- a/src/PhpWord/Writer/Word2007/Element/Shape.php +++ b/src/PhpWord/Writer/Word2007/Element/Shape.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -32,8 +32,6 @@ class Shape extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { @@ -55,6 +53,7 @@ class Shape extends AbstractElement if (!$this->withoutP) { $xmlWriter->startElement('w:p'); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:pict'); @@ -80,7 +79,6 @@ class Shape extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style - * @return void */ private function writeArc(XMLWriter $xmlWriter, ShapeStyle $style) { @@ -95,7 +93,6 @@ class Shape extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style - * @return void */ private function writeCurve(XMLWriter $xmlWriter, ShapeStyle $style) { @@ -111,7 +108,6 @@ class Shape extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style - * @return void */ private function writeLine(XMLWriter $xmlWriter, ShapeStyle $style) { @@ -126,7 +122,6 @@ class Shape extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style - * @return void */ private function writePolyline(XMLWriter $xmlWriter, ShapeStyle $style) { @@ -138,7 +133,6 @@ class Shape extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style - * @return void */ private function writeRoundRect(XMLWriter $xmlWriter, ShapeStyle $style) { @@ -160,12 +154,12 @@ class Shape extends AbstractElement case 'arc': case 'line': $points = explode(' ', $value); - @list($start, $end) = $points; + list($start, $end) = array_pad($points, 2, null); $points = array('start' => $start, 'end' => $end); break; case 'curve': $points = explode(' ', $value); - @list($start, $end, $point1, $point2) = $points; + list($start, $end, $point1, $point2) = array_pad($points, 4, null); $points = array('start' => $start, 'end' => $end, 'point1' => $point1, 'point2' => $point2); break; } diff --git a/src/PhpWord/Writer/Word2007/Element/TOC.php b/src/PhpWord/Writer/Word2007/Element/TOC.php index 996edb64..94437cbf 100644 --- a/src/PhpWord/Writer/Word2007/Element/TOC.php +++ b/src/PhpWord/Writer/Word2007/Element/TOC.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -19,7 +19,6 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\TOC as TOCElement; -use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter; use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter; @@ -34,8 +33,6 @@ class TOC extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { @@ -71,7 +68,6 @@ class TOC extends AbstractElement * @param \PhpOffice\PhpWord\Element\TOC $element * @param \PhpOffice\PhpWord\Element\Title $title * @param bool $writeFieldMark - * @return void */ private function writeTitle(XMLWriter $xmlWriter, TOCElement $element, $title, $writeFieldMark) { @@ -100,13 +96,9 @@ class TOC extends AbstractElement $styleWriter = new FontStyleWriter($xmlWriter, $fontStyle); $styleWriter->write(); } - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->writeElement('w:t', $title->getText()); - } else { - $xmlWriter->startElement('w:t'); - $xmlWriter->writeRaw($title->getText()); - $xmlWriter->endElement(); - } + $xmlWriter->startElement('w:t'); + $this->writeText($title->getText()); + $xmlWriter->endElement(); // w:t $xmlWriter->endElement(); // w:r $xmlWriter->startElement('w:r'); @@ -143,7 +135,6 @@ class TOC extends AbstractElement * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\TOC $element * @param int $indent - * @return void */ private function writeStyle(XMLWriter $xmlWriter, TOCElement $element, $indent) { @@ -189,7 +180,6 @@ class TOC extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\TOC $element - * @return void */ private function writeFieldMark(XMLWriter $xmlWriter, TOCElement $element) { diff --git a/src/PhpWord/Writer/Word2007/Element/Table.php b/src/PhpWord/Writer/Word2007/Element/Table.php index 093666ee..c365b028 100644 --- a/src/PhpWord/Writer/Word2007/Element/Table.php +++ b/src/PhpWord/Writer/Word2007/Element/Table.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -36,8 +36,6 @@ class Table extends AbstractElement { /** * Write element. - * - * @return void */ public function write() { @@ -75,25 +73,10 @@ class Table extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Table $element - * @return void */ private function writeColumns(XMLWriter $xmlWriter, TableElement $element) { - $rows = $element->getRows(); - $rowCount = count($rows); - - $cellWidths = array(); - for ($i = 0; $i < $rowCount; $i++) { - $row = $rows[$i]; - $cells = $row->getCells(); - if (count($cells) <= count($cellWidths)) { - continue; - } - $cellWidths = array(); - foreach ($cells as $cell) { - $cellWidths[] = $cell->getWidth(); - } - } + $cellWidths = $element->findFirstDefinedCellWidths(); $xmlWriter->startElement('w:tblGrid'); foreach ($cellWidths as $width) { @@ -112,7 +95,6 @@ class Table extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Row $row - * @return void */ private function writeRow(XMLWriter $xmlWriter, RowElement $row) { @@ -139,11 +121,9 @@ class Table extends AbstractElement * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Cell $cell - * @return void */ private function writeCell(XMLWriter $xmlWriter, CellElement $cell) { - $xmlWriter->startElement('w:tc'); // Write style diff --git a/src/PhpWord/Writer/Word2007/Element/TableAlignment.php b/src/PhpWord/Writer/Word2007/Element/TableAlignment.php index 45459a38..f44e9ebe 100644 --- a/src/PhpWord/Writer/Word2007/Element/TableAlignment.php +++ b/src/PhpWord/Writer/Word2007/Element/TableAlignment.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,7 +29,7 @@ class TableAlignment /** * @since 0.13.0 * - * @param string $value Any value provided by JcTable simple type. + * @param string $value Any value provided by JcTable simple type * * @see \PhpOffice\PhpWord\SimpleType\JcTable For the allowed values of $value parameter. */ diff --git a/src/PhpWord/Writer/Word2007/Element/Text.php b/src/PhpWord/Writer/Word2007/Element/Text.php index 2df4892b..f421c1af 100644 --- a/src/PhpWord/Writer/Word2007/Element/Text.php +++ b/src/PhpWord/Writer/Word2007/Element/Text.php @@ -10,14 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Element\TrackChange; /** * Text element writer @@ -28,8 +28,6 @@ class Text extends AbstractElement { /** * Write text element. - * - * @return void */ public function write() { @@ -41,20 +39,66 @@ class Text extends AbstractElement $this->startElementP(); + $this->writeOpeningTrackChange(); + $xmlWriter->startElement('w:r'); $this->writeFontStyle(); - $xmlWriter->startElement('w:t'); - $xmlWriter->writeAttribute('xml:space', 'preserve'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->text($this->getText($element->getText())); - } else { - $xmlWriter->writeRaw($this->getText($element->getText())); + $textElement = 'w:t'; + //'w:delText' in case of deleted text + $changed = $element->getTrackChange(); + if ($changed != null && $changed->getChangeType() == TrackChange::DELETED) { + $textElement = 'w:delText'; } + $xmlWriter->startElement($textElement); + + $xmlWriter->writeAttribute('xml:space', 'preserve'); + $this->writeText($this->getText($element->getText())); $xmlWriter->endElement(); $xmlWriter->endElement(); // w:r + $this->writeClosingTrackChange(); + $this->endElementP(); // w:p } + + /** + * Write opening of changed element + */ + protected function writeOpeningTrackChange() + { + $changed = $this->getElement()->getTrackChange(); + if ($changed == null) { + return; + } + + $xmlWriter = $this->getXmlWriter(); + + if (($changed->getChangeType() == TrackChange::INSERTED)) { + $xmlWriter->startElement('w:ins'); + } elseif ($changed->getChangeType() == TrackChange::DELETED) { + $xmlWriter->startElement('w:del'); + } + $xmlWriter->writeAttribute('w:author', $changed->getAuthor()); + if ($changed->getDate() != null) { + $xmlWriter->writeAttribute('w:date', $changed->getDate()->format('Y-m-d\TH:i:s\Z')); + } + $xmlWriter->writeAttribute('w:id', $this->getElement()->getElementId()); + } + + /** + * Write ending + */ + protected function writeClosingTrackChange() + { + $changed = $this->getElement()->getTrackChange(); + if ($changed == null) { + return; + } + + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->endElement(); // w:ins|w:del + } } diff --git a/src/PhpWord/Writer/Word2007/Element/TextBox.php b/src/PhpWord/Writer/Word2007/Element/TextBox.php index 3c4f48c2..9dd4bc3e 100644 --- a/src/PhpWord/Writer/Word2007/Element/TextBox.php +++ b/src/PhpWord/Writer/Word2007/Element/TextBox.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class TextBox extends Image { /** * Write element. - * - * @return void */ public function write() { @@ -45,6 +43,7 @@ class TextBox extends Image $xmlWriter->startElement('w:p'); $styleWriter->writeAlignment(); } + $this->writeCommentRangeStart(); $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:pict'); diff --git a/src/PhpWord/Writer/Word2007/Element/TextBreak.php b/src/PhpWord/Writer/Word2007/Element/TextBreak.php index a9e6f613..7b3d9997 100644 --- a/src/PhpWord/Writer/Word2007/Element/TextBreak.php +++ b/src/PhpWord/Writer/Word2007/Element/TextBreak.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class TextBreak extends Text { /** * Write text break element. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/TextRun.php b/src/PhpWord/Writer/Word2007/Element/TextRun.php index 1e95ab5c..e46ad3f5 100644 --- a/src/PhpWord/Writer/Word2007/Element/TextRun.php +++ b/src/PhpWord/Writer/Word2007/Element/TextRun.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class TextRun extends Text { /** * Write textrun element. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Element/Title.php b/src/PhpWord/Writer/Word2007/Element/Title.php index 925d4c43..6a05a34d 100644 --- a/src/PhpWord/Writer/Word2007/Element/Title.php +++ b/src/PhpWord/Writer/Word2007/Element/Title.php @@ -10,15 +10,13 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Settings; - /** * TextRun element writer * @@ -28,8 +26,6 @@ class Title extends AbstractElement { /** * Write title element. - * - * @return void */ public function write() { @@ -51,31 +47,37 @@ class Title extends AbstractElement $xmlWriter->endElement(); } - $rId = $element->getRelationId(); - $bookmarkRId = $element->getPhpWord()->addBookmark(); + $bookmarkRId = null; + if ($element->getDepth() !== 0) { + $rId = $element->getRelationId(); + $bookmarkRId = $element->getPhpWord()->addBookmark(); - // Bookmark start for TOC - $xmlWriter->startElement('w:bookmarkStart'); - $xmlWriter->writeAttribute('w:id', $bookmarkRId); - $xmlWriter->writeAttribute('w:name', "_Toc{$rId}"); - $xmlWriter->endElement(); + // Bookmark start for TOC + $xmlWriter->startElement('w:bookmarkStart'); + $xmlWriter->writeAttribute('w:id', $bookmarkRId); + $xmlWriter->writeAttribute('w:name', "_Toc{$rId}"); + $xmlWriter->endElement(); //w:bookmarkStart + } // Actual text - $xmlWriter->startElement('w:r'); - if (Settings::isOutputEscapingEnabled()) { - $xmlWriter->writeElement('w:t', $this->getText($element->getText())); - } else { + $text = $element->getText(); + if (is_string($text)) { + $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:t'); - $xmlWriter->writeRaw($this->getText($element->getText())); - $xmlWriter->endElement(); + $this->writeText($text); + $xmlWriter->endElement(); // w:t + $xmlWriter->endElement(); // w:r + } elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) { + $containerWriter = new Container($xmlWriter, $text); + $containerWriter->write(); } - $xmlWriter->endElement(); - // Bookmark end - $xmlWriter->startElement('w:bookmarkEnd'); - $xmlWriter->writeAttribute('w:id', $bookmarkRId); - $xmlWriter->endElement(); - - $xmlWriter->endElement(); + if ($element->getDepth() !== 0) { + // Bookmark end + $xmlWriter->startElement('w:bookmarkEnd'); + $xmlWriter->writeAttribute('w:id', $bookmarkRId); + $xmlWriter->endElement(); //w:bookmarkEnd + } + $xmlWriter->endElement(); //w:p } } diff --git a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php index 26734aa0..ce4e41cb 100644 --- a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php +++ b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -50,7 +50,6 @@ abstract class AbstractPart * Set parent writer. * * @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer - * @return void */ public function setParentWriter(AbstractWriter $writer = null) { @@ -60,17 +59,15 @@ abstract class AbstractPart /** * Get parent writer * - * @return \PhpOffice\PhpWord\Writer\AbstractWriter - * * @throws \PhpOffice\PhpWord\Exception\Exception + * @return \PhpOffice\PhpWord\Writer\AbstractWriter */ public function getParentWriter() { if (!is_null($this->parentWriter)) { return $this->parentWriter; - } else { - throw new Exception('No parent WriterInterface assigned.'); } + throw new Exception('No parent WriterInterface assigned.'); } /** @@ -88,8 +85,23 @@ abstract class AbstractPart } if ($useDiskCaching) { return new XMLWriter(XMLWriter::STORAGE_DISK, $this->parentWriter->getDiskCachingDirectory(), Settings::hasCompatibility()); - } else { - return new XMLWriter(XMLWriter::STORAGE_MEMORY, './', Settings::hasCompatibility()); } + + return new XMLWriter(XMLWriter::STORAGE_MEMORY, './', Settings::hasCompatibility()); + } + + /** + * Write an XML text, this will call text() or writeRaw() depending on the value of Settings::isOutputEscapingEnabled() + * + * @param string $content The text string to write + * @return bool Returns true on success or false on failure + */ + protected function writeText($content) + { + if (Settings::isOutputEscapingEnabled()) { + return $this->getXmlWriter()->text($content); + } + + return $this->getXmlWriter()->writeRaw($content); } } diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 3dd3968b..812d3bf1 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,14 +24,14 @@ use PhpOffice\PhpWord\Element\Chart as ChartElement; * Word2007 chart part writer: word/charts/chartx.xml * * @since 0.12.0 - * @link http://www.datypic.com/sc/ooxml/e-draw-chart_chartSpace.html + * @see http://www.datypic.com/sc/ooxml/e-draw-chart_chartSpace.html */ class Chart extends AbstractPart { /** * Chart element * - * @var \PhpOffice\PhpWord\Element\Chart $element + * @var \PhpOffice\PhpWord\Element\Chart */ private $element; @@ -41,14 +41,18 @@ class Chart extends AbstractPart * @var array */ private $types = array( - 'pie' => array('type' => 'pie', 'colors' => 1), - 'doughnut' => array('type' => 'doughnut', 'colors' => 1, 'hole' => 75, 'no3d' => true), - 'bar' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'bar'), - 'column' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'col'), - 'line' => array('type' => 'line', 'colors' => 0, 'axes' => true), - 'area' => array('type' => 'area', 'colors' => 0, 'axes' => true), - 'radar' => array('type' => 'radar', 'colors' => 0, 'axes' => true, 'radar' => 'standard', 'no3d' => true), - 'scatter' => array('type' => 'scatter', 'colors' => 0, 'axes' => true, 'scatter' => 'marker', 'no3d' => true), + 'pie' => array('type' => 'pie', 'colors' => 1), + 'doughnut' => array('type' => 'doughnut', 'colors' => 1, 'hole' => 75, 'no3d' => true), + 'bar' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'bar', 'grouping' => 'clustered'), + 'stacked_bar' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'bar', 'grouping' => 'stacked'), + 'percent_stacked_bar' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'bar', 'grouping' => 'percentStacked'), + 'column' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'col', 'grouping' => 'clustered'), + 'stacked_column' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'col', 'grouping' => 'stacked'), + 'percent_stacked_column' => array('type' => 'bar', 'colors' => 0, 'axes' => true, 'bar' => 'col', 'grouping' => 'percentStacked'), + 'line' => array('type' => 'line', 'colors' => 0, 'axes' => true), + 'area' => array('type' => 'area', 'colors' => 0, 'axes' => true), + 'radar' => array('type' => 'radar', 'colors' => 0, 'axes' => true, 'radar' => 'standard', 'no3d' => true), + 'scatter' => array('type' => 'scatter', 'colors' => 0, 'axes' => true, 'scatter' => 'marker', 'no3d' => true), ); /** @@ -62,7 +66,6 @@ class Chart extends AbstractPart * Set chart element. * * @param \PhpOffice\PhpWord\Element\Chart $element - * @return void */ public function setElement(ChartElement $element) { @@ -95,16 +98,13 @@ class Chart extends AbstractPart /** * Write chart * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Chart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Chart.html * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writeChart(XMLWriter $xmlWriter) { $xmlWriter->startElement('c:chart'); - $xmlWriter->writeElementBlock('c:autoTitleDeleted', 'val', 1); - $this->writePlotArea($xmlWriter); $xmlWriter->endElement(); // c:chart @@ -113,16 +113,15 @@ class Chart extends AbstractPart /** * Write plot area. * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PlotArea.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PieChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_DoughnutChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_BarChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_LineChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PlotArea.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_PieChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_DoughnutChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_BarChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_LineChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @return void */ private function writePlotArea(XMLWriter $xmlWriter) { @@ -130,12 +129,40 @@ class Chart extends AbstractPart $style = $this->element->getStyle(); $this->options = $this->types[$type]; + $title = $style->getTitle(); + $showLegend = $style->isShowLegend(); + + //Chart title + if ($title) { + $xmlWriter->startElement('c:title'); + $xmlWriter->startElement('c:tx'); + $xmlWriter->startElement('c:rich'); + $xmlWriter->writeRaw(' + + + + + ' . $title . ' + + '); + $xmlWriter->endElement(); // c:rich + $xmlWriter->endElement(); // c:tx + $xmlWriter->endElement(); // c:title + } else { + $xmlWriter->writeElementBlock('c:autoTitleDeleted', 'val', 1); + } + + //Chart legend + if ($showLegend) { + $xmlWriter->writeRaw(''); + } + $xmlWriter->startElement('c:plotArea'); $xmlWriter->writeElement('c:layout'); // Chart $chartType = $this->options['type']; - $chartType .= $style->is3d() && !isset($this->options['no3d'])? '3D' : ''; + $chartType .= $style->is3d() && !isset($this->options['no3d']) ? '3D' : ''; $chartType .= 'Chart'; $xmlWriter->startElement("c:{$chartType}"); @@ -148,7 +175,7 @@ class Chart extends AbstractPart } if (isset($this->options['bar'])) { $xmlWriter->writeElementBlock('c:barDir', 'val', $this->options['bar']); // bar|col - $xmlWriter->writeElementBlock('c:grouping', 'val', 'clustered'); // 3d; standard = percentStacked + $xmlWriter->writeElementBlock('c:grouping', 'val', $this->options['grouping']); // 3d; standard = percentStacked } if (isset($this->options['radar'])) { $xmlWriter->writeElementBlock('c:radarStyle', 'val', $this->options['radar']); @@ -160,6 +187,8 @@ class Chart extends AbstractPart // Series $this->writeSeries($xmlWriter, isset($this->options['scatter'])); + $xmlWriter->writeElementBlock('c:overlap', 'val', '100'); + // Axes if (isset($this->options['axes'])) { $xmlWriter->writeElementBlock('c:axId', 'val', 1); @@ -182,11 +211,12 @@ class Chart extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param bool $scatter - * @return void */ private function writeSeries(XMLWriter $xmlWriter, $scatter = false) { $series = $this->element->getSeries(); + $style = $this->element->getStyle(); + $colors = $style->getColors(); $index = 0; foreach ($series as $seriesItem) { @@ -198,6 +228,32 @@ class Chart extends AbstractPart $xmlWriter->writeElementBlock('c:idx', 'val', $index); $xmlWriter->writeElementBlock('c:order', 'val', $index); + if (!is_null($seriesItem['name']) && $seriesItem['name'] != '') { + $xmlWriter->startElement('c:tx'); + $xmlWriter->startElement('c:strRef'); + $xmlWriter->startElement('c:strCache'); + $xmlWriter->writeElementBlock('c:ptCount', 'val', 1); + $xmlWriter->startElement('c:pt'); + $xmlWriter->writeAttribute('idx', 0); + $xmlWriter->startElement('c:v'); + $xmlWriter->writeRaw($seriesItem['name']); + $xmlWriter->endElement(); // c:v + $xmlWriter->endElement(); // c:pt + $xmlWriter->endElement(); // c:strCache + $xmlWriter->endElement(); // c:strRef + $xmlWriter->endElement(); // c:tx + } + + // The c:dLbls was added to make word charts look more like the reports in SurveyGizmo + // This section needs to be made configurable before a pull request is made + $xmlWriter->startElement('c:dLbls'); + + foreach ($style->getDataLabelOptions() as $option => $val) { + $xmlWriter->writeElementBlock("c:{$option}", 'val', (int) $val); + } + + $xmlWriter->endElement(); // c:dLbls + if (isset($this->options['scatter'])) { $this->writeShape($xmlWriter); } @@ -208,12 +264,31 @@ class Chart extends AbstractPart } else { $this->writeSeriesItem($xmlWriter, 'cat', $categories); $this->writeSeriesItem($xmlWriter, 'val', $values); + + // setting the chart colors was taken from https://github.com/PHPOffice/PHPWord/issues/494 + if (is_array($colors) && count($colors)) { + // This is a workaround to make each series in a stack chart use a different color + if ($this->options['type'] == 'bar' && stristr($this->options['grouping'], 'stacked')) { + array_shift($colors); + } + $colorIndex = 0; + foreach ($colors as $color) { + $xmlWriter->startElement('c:dPt'); + $xmlWriter->writeElementBlock('c:idx', 'val', $colorIndex); + $xmlWriter->startElement('c:spPr'); + $xmlWriter->startElement('a:solidFill'); + $xmlWriter->writeElementBlock('a:srgbClr', 'val', $color); + $xmlWriter->endElement(); // a:solidFill + $xmlWriter->endElement(); // c:spPr + $xmlWriter->endElement(); // c:dPt + $colorIndex++; + } + } } $xmlWriter->endElement(); // c:ser $index++; } - } /** @@ -222,13 +297,12 @@ class Chart extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $type * @param array $values - * @return void */ private function writeSeriesItem(XMLWriter $xmlWriter, $type, $values) { $types = array( - 'cat' => array('c:cat', 'c:strLit'), - 'val' => array('c:val', 'c:numLit'), + 'cat' => array('c:cat', 'c:strLit'), + 'val' => array('c:val', 'c:numLit'), 'xVal' => array('c:xVal', 'c:strLit'), 'yVal' => array('c:yVal', 'c:numLit'), ); @@ -236,6 +310,7 @@ class Chart extends AbstractPart $xmlWriter->startElement($itemType); $xmlWriter->startElement($itemLit); + $xmlWriter->writeElementBlock('c:ptCount', 'val', count($values)); $index = 0; foreach ($values as $value) { @@ -246,7 +321,7 @@ class Chart extends AbstractPart } else { $xmlWriter->startElement('c:v'); $xmlWriter->writeRaw($value); - $xmlWriter->endElement(); + $xmlWriter->endElement(); // c:v } $xmlWriter->endElement(); // c:pt $index++; @@ -259,13 +334,13 @@ class Chart extends AbstractPart /** * Write axis * - * @link http://www.datypic.com/sc/ooxml/t-draw-chart_CT_CatAx.html + * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_CatAx.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $type - * @return void */ private function writeAxis(XMLWriter $xmlWriter, $type) { + $style = $this->element->getStyle(); $types = array( 'cat' => array('c:catAx', 1, 'b', 2), 'val' => array('c:valAx', 2, 'l', 1), @@ -276,17 +351,39 @@ class Chart extends AbstractPart $xmlWriter->writeElementBlock('c:axId', 'val', $axisId); $xmlWriter->writeElementBlock('c:axPos', 'val', $axisPos); + + $categoryAxisTitle = $style->getCategoryAxisTitle(); + $valueAxisTitle = $style->getValueAxisTitle(); + + if ($axisType == 'c:catAx') { + if (!is_null($categoryAxisTitle)) { + $this->writeAxisTitle($xmlWriter, $categoryAxisTitle); + } + } elseif ($axisType == 'c:valAx') { + if (!is_null($valueAxisTitle)) { + $this->writeAxisTitle($xmlWriter, $valueAxisTitle); + } + } + $xmlWriter->writeElementBlock('c:crossAx', 'val', $axisCross); $xmlWriter->writeElementBlock('c:auto', 'val', 1); if (isset($this->options['axes'])) { $xmlWriter->writeElementBlock('c:delete', 'val', 0); - $xmlWriter->writeElementBlock('c:majorTickMark', 'val', 'none'); + $xmlWriter->writeElementBlock('c:majorTickMark', 'val', $style->getMajorTickPosition()); $xmlWriter->writeElementBlock('c:minorTickMark', 'val', 'none'); - $xmlWriter->writeElementBlock('c:tickLblPos', 'val', 'none'); // nextTo + if ($style->showAxisLabels()) { + if ($axisType == 'c:catAx') { + $xmlWriter->writeElementBlock('c:tickLblPos', 'val', $style->getCategoryLabelPosition()); + } else { + $xmlWriter->writeElementBlock('c:tickLblPos', 'val', $style->getValueLabelPosition()); + } + } else { + $xmlWriter->writeElementBlock('c:tickLblPos', 'val', 'none'); + } $xmlWriter->writeElementBlock('c:crosses', 'val', 'autoZero'); } - if (isset($this->options['radar'])) { + if (isset($this->options['radar']) || ($type == 'cat' && $style->showGridX()) || ($type == 'val' && $style->showGridY())) { $xmlWriter->writeElement('c:majorGridlines'); } @@ -302,10 +399,9 @@ class Chart extends AbstractPart /** * Write shape * - * @link http://www.datypic.com/sc/ooxml/t-a_CT_ShapeProperties.html + * @see http://www.datypic.com/sc/ooxml/t-a_CT_ShapeProperties.html * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param bool $line - * @return void */ private function writeShape(XMLWriter $xmlWriter, $line = false) { @@ -319,4 +415,30 @@ class Chart extends AbstractPart $xmlWriter->endElement(); // a:ln $xmlWriter->endElement(); // c:spPr } + + private function writeAxisTitle(XMLWriter $xmlWriter, $title) + { + $xmlWriter->startElement('c:title'); //start c:title + $xmlWriter->startElement('c:tx'); //start c:tx + $xmlWriter->startElement('c:rich'); // start c:rich + $xmlWriter->writeElement('a:bodyPr'); + $xmlWriter->writeElement('a:lstStyle'); + $xmlWriter->startElement('a:p'); + $xmlWriter->startElement('a:pPr'); + $xmlWriter->writeElement('a:defRPr'); + $xmlWriter->endElement(); // end a:pPr + $xmlWriter->startElement('a:r'); + $xmlWriter->writeElementBlock('a:rPr', 'lang', 'en-US'); + + $xmlWriter->startElement('a:t'); + $xmlWriter->writeRaw($title); + $xmlWriter->endElement(); //end a:t + + $xmlWriter->endElement(); // end a:r + $xmlWriter->endElement(); //end a:p + $xmlWriter->endElement(); //end c:rich + $xmlWriter->endElement(); // end c:tx + $xmlWriter->writeElementBlock('c:overlay', 'val', '0'); + $xmlWriter->endElement(); // end c:title + } } diff --git a/src/PhpWord/Writer/Word2007/Part/Comments.php b/src/PhpWord/Writer/Word2007/Part/Comments.php new file mode 100644 index 00000000..33c9f59e --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Part/Comments.php @@ -0,0 +1,104 @@ +getXmlWriter(); + + $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); + $xmlWriter->startElement('w:comments'); + $xmlWriter->writeAttribute('xmlns:ve', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + $xmlWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office'); + $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $xmlWriter->writeAttribute('xmlns:m', 'http://schemas.openxmlformats.org/officeDocument/2006/math'); + $xmlWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml'); + $xmlWriter->writeAttribute('xmlns:wp', 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing'); + $xmlWriter->writeAttribute('xmlns:w10', 'urn:schemas-microsoft-com:office:word'); + $xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'); + $xmlWriter->writeAttribute('xmlns:wne', 'http://schemas.microsoft.com/office/word/2006/wordml'); + + if ($this->elements !== null) { + foreach ($this->elements as $element) { + if ($element instanceof Comment) { + $this->writeComment($xmlWriter, $element); + } + } + } + + $xmlWriter->endElement(); // w:comments + + return $xmlWriter->getData(); + } + + /** + * Write comment item. + * + * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Element\Comment $comment + */ + protected function writeComment(XMLWriter $xmlWriter, Comment $comment) + { + $xmlWriter->startElement('w:comment'); + $xmlWriter->writeAttribute('w:id', $comment->getElementId()); + $xmlWriter->writeAttribute('w:author', $comment->getAuthor()); + if ($comment->getDate() != null) { + $xmlWriter->writeAttribute('w:date', $comment->getDate()->format($this->dateFormat)); + } + $xmlWriter->writeAttributeIf($comment->getInitials() != null, 'w:initials', $comment->getInitials()); + + $containerWriter = new Container($xmlWriter, $comment); + $containerWriter->write(); + + $xmlWriter->endElement(); // w:comment + } + + /** + * Set element + * + * @param \PhpOffice\PhpWord\Element\Comment[] $elements + * @return self + */ + public function setElements($elements) + { + $this->elements = $elements; + + return $this; + } +} diff --git a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php index 1c81f343..28a2d294 100644 --- a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -36,19 +36,20 @@ class ContentTypes extends AbstractPart $contentTypes = $parentWriter->getContentTypes(); $openXMLPrefix = 'application/vnd.openxmlformats-'; - $wordMLPrefix = $openXMLPrefix . 'officedocument.wordprocessingml.'; - $drawingMLPrefix = $openXMLPrefix . 'officedocument.drawingml.'; + $wordMLPrefix = $openXMLPrefix . 'officedocument.wordprocessingml.'; + $drawingMLPrefix = $openXMLPrefix . 'officedocument.drawingml.'; $overrides = array( '/docProps/core.xml' => $openXMLPrefix . 'package.core-properties+xml', '/docProps/app.xml' => $openXMLPrefix . 'officedocument.extended-properties+xml', '/docProps/custom.xml' => $openXMLPrefix . 'officedocument.custom-properties+xml', - '/word/document.xml' => $wordMLPrefix . 'document.main+xml', - '/word/styles.xml' => $wordMLPrefix . 'styles+xml', - '/word/numbering.xml' => $wordMLPrefix . 'numbering+xml', - '/word/settings.xml' => $wordMLPrefix . 'settings+xml', + '/word/document.xml' => $wordMLPrefix . 'document.main+xml', + '/word/styles.xml' => $wordMLPrefix . 'styles+xml', + '/word/numbering.xml' => $wordMLPrefix . 'numbering+xml', + '/word/settings.xml' => $wordMLPrefix . 'settings+xml', '/word/theme/theme1.xml' => $openXMLPrefix . 'officedocument.theme+xml', - '/word/webSettings.xml' => $wordMLPrefix . 'webSettings+xml', - '/word/fontTable.xml' => $wordMLPrefix . 'fontTable+xml', + '/word/webSettings.xml' => $wordMLPrefix . 'webSettings+xml', + '/word/fontTable.xml' => $wordMLPrefix . 'fontTable+xml', + '/word/comments.xml' => $wordMLPrefix . 'comments+xml', ); $defaults = $contentTypes['default']; @@ -81,8 +82,7 @@ class ContentTypes extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter XML Writer * @param array $parts - * @param boolean $isDefault - * @return void + * @param bool $isDefault */ private function writeContentType(XMLWriter $xmlWriter, $parts, $isDefault) { diff --git a/src/PhpWord/Writer/Word2007/Part/DocPropsApp.php b/src/PhpWord/Writer/Word2007/Part/DocPropsApp.php index adfe752f..3452d864 100644 --- a/src/PhpWord/Writer/Word2007/Part/DocPropsApp.php +++ b/src/PhpWord/Writer/Word2007/Part/DocPropsApp.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php b/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php index afb6f286..caefbd86 100644 --- a/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php +++ b/src/PhpWord/Writer/Word2007/Part/DocPropsCore.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php b/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php index 63ed8ede..478075d3 100644 --- a/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php +++ b/src/PhpWord/Writer/Word2007/Part/DocPropsCustom.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -60,7 +60,11 @@ class DocPropsCustom extends AbstractPart $xmlWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false'); break; case 'd': - $xmlWriter->writeElement('vt:filetime', date($this->dateFormat, $propertyValue)); + if ($propertyValue instanceof \DateTime) { + $xmlWriter->writeElement('vt:filetime', $propertyValue->format($this->dateFormat)); + } else { + $xmlWriter->writeElement('vt:filetime', date($this->dateFormat, $propertyValue)); + } break; default: $xmlWriter->writeElement('vt:lpwstr', $propertyValue); diff --git a/src/PhpWord/Writer/Word2007/Part/Document.php b/src/PhpWord/Writer/Word2007/Part/Document.php index 411946f5..986b4985 100644 --- a/src/PhpWord/Writer/Word2007/Part/Document.php +++ b/src/PhpWord/Writer/Word2007/Part/Document.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -56,7 +56,6 @@ class Document extends AbstractPart $xmlWriter->startElement('w:body'); - if ($sectionCount > 0) { foreach ($sections as $section) { $currentSection++; @@ -83,7 +82,6 @@ class Document extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Section $section - * @return void */ private function writeSection(XMLWriter $xmlWriter, Section $section) { @@ -99,7 +97,6 @@ class Document extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Section $section - * @return void */ private function writeSectionSettings(XMLWriter $xmlWriter, Section $section) { @@ -129,6 +126,32 @@ class Document extends AbstractPart $xmlWriter->endElement(); } + //footnote properties + if ($section->getFootnotePropoperties() !== null) { + $xmlWriter->startElement('w:footnotePr'); + if ($section->getFootnotePropoperties()->getPos() != null) { + $xmlWriter->startElement('w:pos'); + $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getPos()); + $xmlWriter->endElement(); + } + if ($section->getFootnotePropoperties()->getNumFmt() != null) { + $xmlWriter->startElement('w:numFmt'); + $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumFmt()); + $xmlWriter->endElement(); + } + if ($section->getFootnotePropoperties()->getNumStart() != null) { + $xmlWriter->startElement('w:numStart'); + $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumStart()); + $xmlWriter->endElement(); + } + if ($section->getFootnotePropoperties()->getNumRestart() != null) { + $xmlWriter->startElement('w:numRestart'); + $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumRestart()); + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); + } + // Section settings $styleWriter = new SectionStyleWriter($xmlWriter, $section->getStyle()); $styleWriter->write(); diff --git a/src/PhpWord/Writer/Word2007/Part/Endnotes.php b/src/PhpWord/Writer/Word2007/Part/Endnotes.php index bc15cf1e..ce3a46bf 100644 --- a/src/PhpWord/Writer/Word2007/Part/Endnotes.php +++ b/src/PhpWord/Writer/Word2007/Part/Endnotes.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/FontTable.php b/src/PhpWord/Writer/Word2007/Part/FontTable.php index 08f0ad0e..1161a951 100644 --- a/src/PhpWord/Writer/Word2007/Part/FontTable.php +++ b/src/PhpWord/Writer/Word2007/Part/FontTable.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/Footer.php b/src/PhpWord/Writer/Word2007/Part/Footer.php index 3e4e4fee..97b47790 100644 --- a/src/PhpWord/Writer/Word2007/Part/Footer.php +++ b/src/PhpWord/Writer/Word2007/Part/Footer.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/Footnotes.php b/src/PhpWord/Writer/Word2007/Part/Footnotes.php index fd692149..59bf1830 100644 --- a/src/PhpWord/Writer/Word2007/Part/Footnotes.php +++ b/src/PhpWord/Writer/Word2007/Part/Footnotes.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -137,7 +137,6 @@ class Footnotes extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Footnote|\PhpOffice\PhpWord\Element\Endnote $element - * @return void */ protected function writeNote(XMLWriter $xmlWriter, $element) { diff --git a/src/PhpWord/Writer/Word2007/Part/Header.php b/src/PhpWord/Writer/Word2007/Part/Header.php index 438e503e..b58df1f9 100644 --- a/src/PhpWord/Writer/Word2007/Part/Header.php +++ b/src/PhpWord/Writer/Word2007/Part/Header.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/Numbering.php b/src/PhpWord/Writer/Word2007/Part/Numbering.php index c5c9b4c7..61e5cc23 100644 --- a/src/PhpWord/Writer/Word2007/Part/Numbering.php +++ b/src/PhpWord/Writer/Word2007/Part/Numbering.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -99,7 +99,6 @@ class Numbering extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level - * @return void */ private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level) { @@ -140,7 +139,6 @@ class Numbering extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level - * @return void * @todo Use paragraph style writer */ private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level) @@ -173,7 +171,6 @@ class Numbering extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level - * @return void * @todo Use font style writer */ private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level) diff --git a/src/PhpWord/Writer/Word2007/Part/Rels.php b/src/PhpWord/Writer/Word2007/Part/Rels.php index 4a3b5b67..661a4fa8 100644 --- a/src/PhpWord/Writer/Word2007/Part/Rels.php +++ b/src/PhpWord/Writer/Word2007/Part/Rels.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -53,7 +53,6 @@ class Rels extends AbstractPart * @param array $xmlRels * @param array $mediaRels * @param int $relId - * @return void */ protected function writeRels(XMLWriter $xmlWriter, $xmlRels = array(), $mediaRels = array(), $relId = 1) { @@ -80,7 +79,6 @@ class Rels extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param int $relId * @param array $mediaRel - * @return void */ private function writeMediaRel(XMLWriter $xmlWriter, $relId, $mediaRel) { @@ -109,8 +107,6 @@ class Rels extends AbstractPart * @param string $target Relationship target * @param string $targetMode Relationship target mode * - * @return void - * * @throws \PhpOffice\PhpWord\Exception\Exception */ private function writeRel(XMLWriter $xmlWriter, $relId, $type, $target, $targetMode = '') @@ -128,7 +124,7 @@ class Rels extends AbstractPart } $xmlWriter->endElement(); } else { - throw new Exception("Invalid parameters passed."); + throw new Exception('Invalid parameters passed.'); } } } diff --git a/src/PhpWord/Writer/Word2007/Part/RelsDocument.php b/src/PhpWord/Writer/Word2007/Part/RelsDocument.php index c60dba28..2a0c5e11 100644 --- a/src/PhpWord/Writer/Word2007/Part/RelsDocument.php +++ b/src/PhpWord/Writer/Word2007/Part/RelsDocument.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/RelsPart.php b/src/PhpWord/Writer/Word2007/Part/RelsPart.php index e8939c7f..ac61a0c2 100644 --- a/src/PhpWord/Writer/Word2007/Part/RelsPart.php +++ b/src/PhpWord/Writer/Word2007/Part/RelsPart.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Part/Settings.php b/src/PhpWord/Writer/Word2007/Part/Settings.php index d881e13a..b764642a 100644 --- a/src/PhpWord/Writer/Word2007/Part/Settings.php +++ b/src/PhpWord/Writer/Word2007/Part/Settings.php @@ -10,17 +10,22 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Part; +use PhpOffice\Common\Microsoft\PasswordEncoder; +use PhpOffice\PhpWord\ComplexType\ProofState; +use PhpOffice\PhpWord\ComplexType\TrackChangesView; +use PhpOffice\PhpWord\Style\Language; + /** * Word2007 settings part writer: word/settings.xml * - * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_Settings.html + * @see http://www.schemacentral.com/sc/ooxml/t-w_CT_Settings.html */ class Settings extends AbstractPart { @@ -67,13 +72,12 @@ class Settings extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $settingKey * @param array|string $settingValue - * @return void */ protected function writeSetting($xmlWriter, $settingKey, $settingValue) { if ($settingValue == '') { $xmlWriter->writeElement($settingKey); - } else { + } elseif (is_array($settingValue) && !empty($settingValue)) { $xmlWriter->startElement($settingKey); /** @var array $settingValue Type hint */ @@ -92,89 +96,233 @@ class Settings extends AbstractPart /** * Get settings. - * - * @return void */ private function getSettings() { + /** @var \PhpOffice\PhpWord\Metadata\Settings $documentSettings */ + $documentSettings = $this->getParentWriter()->getPhpWord()->getSettings(); + // Default settings $this->settings = array( - 'w:zoom' => array('@attributes' => array('w:percent' => '100')), - 'w:defaultTabStop' => array('@attributes' => array('w:val' => '708')), - 'w:hyphenationZone' => array('@attributes' => array('w:val' => '425')), + 'w:defaultTabStop' => array('@attributes' => array('w:val' => '708')), + 'w:hyphenationZone' => array('@attributes' => array('w:val' => '425')), 'w:characterSpacingControl' => array('@attributes' => array('w:val' => 'doNotCompress')), - 'w:themeFontLang' => array('@attributes' => array('w:val' => 'en-US')), - 'w:decimalSymbol' => array('@attributes' => array('w:val' => '.')), - 'w:listSeparator' => array('@attributes' => array('w:val' => ';')), - 'w:compat' => array(), - 'm:mathPr' => array( - 'm:mathFont' => array('@attributes' => array('m:val' => 'Cambria Math')), - 'm:brkBin' => array('@attributes' => array('m:val' => 'before')), - 'm:brkBinSub' => array('@attributes' => array('m:val' => '--')), - 'm:smallFrac' => array('@attributes' => array('m:val' => 'off')), - 'm:dispDef' => '', - 'm:lMargin' => array('@attributes' => array('m:val' => '0')), - 'm:rMargin' => array('@attributes' => array('m:val' => '0')), - 'm:defJc' => array('@attributes' => array('m:val' => 'centerGroup')), + 'w:decimalSymbol' => array('@attributes' => array('w:val' => $documentSettings->getDecimalSymbol())), + 'w:listSeparator' => array('@attributes' => array('w:val' => ';')), + 'w:compat' => array(), + 'm:mathPr' => array( + 'm:mathFont' => array('@attributes' => array('m:val' => 'Cambria Math')), + 'm:brkBin' => array('@attributes' => array('m:val' => 'before')), + 'm:brkBinSub' => array('@attributes' => array('m:val' => '--')), + 'm:smallFrac' => array('@attributes' => array('m:val' => 'off')), + 'm:dispDef' => '', + 'm:lMargin' => array('@attributes' => array('m:val' => '0')), + 'm:rMargin' => array('@attributes' => array('m:val' => '0')), + 'm:defJc' => array('@attributes' => array('m:val' => 'centerGroup')), 'm:wrapIndent' => array('@attributes' => array('m:val' => '1440')), - 'm:intLim' => array('@attributes' => array('m:val' => 'subSup')), - 'm:naryLim' => array('@attributes' => array('m:val' => 'undOvr')), + 'm:intLim' => array('@attributes' => array('m:val' => 'subSup')), + 'm:naryLim' => array('@attributes' => array('m:val' => 'undOvr')), ), 'w:clrSchemeMapping' => array( '@attributes' => array( - 'w:bg1' => 'light1', - 'w:t1' => 'dark1', - 'w:bg2' => 'light2', - 'w:t2' => 'dark2', - 'w:accent1' => 'accent1', - 'w:accent2' => 'accent2', - 'w:accent3' => 'accent3', - 'w:accent4' => 'accent4', - 'w:accent5' => 'accent5', - 'w:accent6' => 'accent6', - 'w:hyperlink' => 'hyperlink', + 'w:bg1' => 'light1', + 'w:t1' => 'dark1', + 'w:bg2' => 'light2', + 'w:t2' => 'dark2', + 'w:accent1' => 'accent1', + 'w:accent2' => 'accent2', + 'w:accent3' => 'accent3', + 'w:accent4' => 'accent4', + 'w:accent5' => 'accent5', + 'w:accent6' => 'accent6', + 'w:hyperlink' => 'hyperlink', 'w:followedHyperlink' => 'followedHyperlink', ), ), ); - // Other settings - $this->getProtection(); - $this->getCompatibility(); + $this->setOnOffValue('w:mirrorMargins', $documentSettings->hasMirrorMargins()); + $this->setOnOffValue('w:hideSpellingErrors', $documentSettings->hasHideSpellingErrors()); + $this->setOnOffValue('w:hideGrammaticalErrors', $documentSettings->hasHideGrammaticalErrors()); + $this->setOnOffValue('w:trackRevisions', $documentSettings->hasTrackRevisions()); + $this->setOnOffValue('w:doNotTrackMoves', $documentSettings->hasDoNotTrackMoves()); + $this->setOnOffValue('w:doNotTrackFormatting', $documentSettings->hasDoNotTrackFormatting()); + $this->setOnOffValue('w:evenAndOddHeaders', $documentSettings->hasEvenAndOddHeaders()); + $this->setOnOffValue('w:updateFields', $documentSettings->hasUpdateFields()); + $this->setOnOffValue('w:autoHyphenation', $documentSettings->hasAutoHyphenation()); + $this->setOnOffValue('w:doNotHyphenateCaps', $documentSettings->hasDoNotHyphenateCaps()); + + $this->setThemeFontLang($documentSettings->getThemeFontLang()); + $this->setRevisionView($documentSettings->getRevisionView()); + $this->setDocumentProtection($documentSettings->getDocumentProtection()); + $this->setProofState($documentSettings->getProofState()); + $this->setZoom($documentSettings->getZoom()); + $this->setConsecutiveHyphenLimit($documentSettings->getConsecutiveHyphenLimit()); + $this->setHyphenationZone($documentSettings->getHyphenationZone()); + $this->setCompatibility(); + } + + /** + * Adds a boolean attribute to the settings array + * + * @param string $settingName + * @param bool|null $booleanValue + */ + private function setOnOffValue($settingName, $booleanValue) + { + if (!is_bool($booleanValue)) { + return; + } + + $value = $booleanValue ? 'true' : 'false'; + $this->settings[$settingName] = array('@attributes' => array('w:val' => $value)); } /** * Get protection settings. * - * @return void + * @param \PhpOffice\PhpWord\Metadata\Protection $documentProtection */ - private function getProtection() + private function setDocumentProtection($documentProtection) { - $protection = $this->getParentWriter()->getPhpWord()->getProtection(); - if ($protection->getEditing() !== null) { - $this->settings['w:documentProtection'] = array( + if ($documentProtection->getEditing() !== null) { + if ($documentProtection->getPassword() == null) { + $this->settings['w:documentProtection'] = array( + '@attributes' => array( + 'w:enforcement' => 1, + 'w:edit' => $documentProtection->getEditing(), + ), + ); + } else { + if ($documentProtection->getSalt() == null) { + $documentProtection->setSalt(openssl_random_pseudo_bytes(16)); + } + $passwordHash = PasswordEncoder::hashPassword($documentProtection->getPassword(), $documentProtection->getAlgorithm(), $documentProtection->getSalt(), $documentProtection->getSpinCount()); + $this->settings['w:documentProtection'] = array( + '@attributes' => array( + 'w:enforcement' => 1, + 'w:edit' => $documentProtection->getEditing(), + 'w:cryptProviderType' => 'rsaFull', + 'w:cryptAlgorithmClass' => 'hash', + 'w:cryptAlgorithmType' => 'typeAny', + 'w:cryptAlgorithmSid' => PasswordEncoder::getAlgorithmId($documentProtection->getAlgorithm()), + 'w:cryptSpinCount' => $documentProtection->getSpinCount(), + 'w:hash' => $passwordHash, + 'w:salt' => base64_encode($documentProtection->getSalt()), + ), + ); + } + } + } + + /** + * Set the Proof state + * + * @param ProofState $proofState + */ + private function setProofState(ProofState $proofState = null) + { + if ($proofState != null && $proofState->getGrammar() !== null && $proofState->getSpelling() !== null) { + $this->settings['w:proofState'] = array( '@attributes' => array( - 'w:enforcement' => 1, - 'w:edit' => $protection->getEditing(), - ) + 'w:spelling' => $proofState->getSpelling(), + 'w:grammar' => $proofState->getGrammar(), + ), ); } } /** - * Get compatibility setting. + * Set the Revision View * - * @return void + * @param TrackChangesView $trackChangesView */ - private function getCompatibility() + private function setRevisionView(TrackChangesView $trackChangesView = null) + { + if ($trackChangesView != null) { + $revisionView = array(); + $revisionView['w:markup'] = $trackChangesView->hasMarkup() ? 'true' : 'false'; + $revisionView['w:comments'] = $trackChangesView->hasComments() ? 'true' : 'false'; + $revisionView['w:insDel'] = $trackChangesView->hasInsDel() ? 'true' : 'false'; + $revisionView['w:formatting'] = $trackChangesView->hasFormatting() ? 'true' : 'false'; + $revisionView['w:inkAnnotations'] = $trackChangesView->hasInkAnnotations() ? 'true' : 'false'; + + $this->settings['w:revisionView'] = array('@attributes' => $revisionView); + } + } + + /** + * Sets the language + * + * @param Language $language + */ + private function setThemeFontLang(Language $language = null) + { + $latinLanguage = ($language == null || $language->getLatin() === null) ? 'en-US' : $language->getLatin(); + $lang = array(); + $lang['w:val'] = $latinLanguage; + if ($language != null) { + $lang['w:eastAsia'] = $language->getEastAsia() === null ? 'x-none' : $language->getEastAsia(); + $lang['w:bidi'] = $language->getBidirectional() === null ? 'x-none' : $language->getBidirectional(); + } + $this->settings['w:themeFontLang'] = array('@attributes' => $lang); + } + + /** + * Set the magnification + * + * @param mixed $zoom + */ + private function setZoom($zoom = null) + { + if ($zoom !== null) { + $attr = is_int($zoom) ? 'w:percent' : 'w:val'; + $this->settings['w:zoom'] = array('@attributes' => array($attr => $zoom)); + } + } + + /** + * @param int|null $consecutiveHyphenLimit + */ + private function setConsecutiveHyphenLimit($consecutiveHyphenLimit) + { + if ($consecutiveHyphenLimit === null) { + return; + } + + $this->settings['w:consecutiveHyphenLimit'] = array( + '@attributes' => array('w:val' => $consecutiveHyphenLimit), + ); + } + + /** + * @param float|null $hyphenationZone + */ + private function setHyphenationZone($hyphenationZone) + { + if ($hyphenationZone === null) { + return; + } + + $this->settings['w:hyphenationZone'] = array( + '@attributes' => array('w:val' => $hyphenationZone), + ); + } + + /** + * Get compatibility setting. + */ + private function setCompatibility() { $compatibility = $this->getParentWriter()->getPhpWord()->getCompatibility(); if ($compatibility->getOoxmlVersion() !== null) { - $this->settings['w:compat']['w:compatSetting'] = array('@attributes' => array( - 'w:name' => 'compatibilityMode', - 'w:uri' => 'http://schemas.microsoft.com/office/word', - 'w:val' => $compatibility->getOoxmlVersion(), - )); + $this->settings['w:compat']['w:compatSetting'] = array( + '@attributes' => array( + 'w:name' => 'compatibilityMode', + 'w:uri' => 'http://schemas.microsoft.com/office/word', + 'w:val' => $compatibility->getOoxmlVersion(), + ), + ); } } } diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php index 7bcb8d92..d05338c7 100644 --- a/src/PhpWord/Writer/Word2007/Part/Styles.php +++ b/src/PhpWord/Writer/Word2007/Part/Styles.php @@ -10,15 +10,14 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\Common\XMLWriter; -use PhpOffice\PhpWord\Settings as PhpWordSettings; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; @@ -79,12 +78,14 @@ class Styles extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\AbstractStyle[] $styles - * @return void */ private function writeDefaultStyles(XMLWriter $xmlWriter, $styles) { - $fontName = PhpWordSettings::getDefaultFontName(); - $fontSize = PhpWordSettings::getDefaultFontSize(); + $phpWord = $this->getParentWriter()->getPhpWord(); + $fontName = $phpWord->getDefaultFontName(); + $fontSize = $phpWord->getDefaultFontSize(); + $language = $phpWord->getSettings()->getThemeFontLang(); + $latinLanguage = ($language == null || $language->getLatin() === null) ? 'en-US' : $language->getLatin(); // Default font $xmlWriter->startElement('w:docDefaults'); @@ -102,6 +103,13 @@ class Styles extends AbstractPart $xmlWriter->startElement('w:szCs'); $xmlWriter->writeAttribute('w:val', $fontSize * 2); $xmlWriter->endElement(); // w:szCs + $xmlWriter->startElement('w:lang'); + $xmlWriter->writeAttribute('w:val', $latinLanguage); + if ($language != null) { + $xmlWriter->writeAttributeIf($language->getEastAsia() !== null, 'w:eastAsia', $language->getEastAsia()); + $xmlWriter->writeAttributeIf($language->getBidirectional() !== null, 'w:bidi', $language->getBidirectional()); + } + $xmlWriter->endElement(); // w:lang $xmlWriter->endElement(); // w:rPr $xmlWriter->endElement(); // w:rPrDefault $xmlWriter->endElement(); // w:docDefaults @@ -115,7 +123,18 @@ class Styles extends AbstractPart $xmlWriter->writeAttribute('w:val', 'Normal'); $xmlWriter->endElement(); // w:name if (isset($styles['Normal'])) { - $styleWriter = new ParagraphStyleWriter($xmlWriter, $styles['Normal']); + $normalStyle = $styles['Normal']; + // w:pPr + if ($normalStyle instanceof Fontstyle && $normalStyle->getParagraph() != null) { + $styleWriter = new ParagraphStyleWriter($xmlWriter, $normalStyle->getParagraph()); + $styleWriter->write(); + } elseif ($normalStyle instanceof ParagraphStyle) { + $styleWriter = new ParagraphStyleWriter($xmlWriter, $normalStyle); + $styleWriter->write(); + } + + // w:rPr + $styleWriter = new FontStyleWriter($xmlWriter, $normalStyle); $styleWriter->write(); } $xmlWriter->endElement(); // w:style @@ -145,7 +164,6 @@ class Styles extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Font $style - * @return void */ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $style) { @@ -162,14 +180,23 @@ class Styles extends AbstractPart // Heading style if ($styleType == 'title') { $arrStyle = explode('_', $styleName); - $styleId = 'Heading' . $arrStyle[1]; - $styleName = 'heading ' . $arrStyle[1]; - $styleLink = 'Heading' . $arrStyle[1] . 'Char'; + if (count($arrStyle) > 1) { + $styleId = 'Heading' . $arrStyle[1]; + $styleName = 'heading ' . $arrStyle[1]; + $styleLink = 'Heading' . $arrStyle[1] . 'Char'; + } else { + $styleId = $styleName; + $styleName = strtolower($styleName); + $styleLink = $styleName . 'Char'; + } $xmlWriter->writeAttribute('w:styleId', $styleId); $xmlWriter->startElement('w:link'); $xmlWriter->writeAttribute('w:val', $styleLink); $xmlWriter->endElement(); + } elseif (!is_null($paragraphStyle)) { + // if type is 'paragraph' it should have a styleId + $xmlWriter->writeAttribute('w:styleId', $styleName); } // Style name @@ -178,7 +205,13 @@ class Styles extends AbstractPart $xmlWriter->endElement(); // Parent style - $xmlWriter->writeElementIf(!is_null($paragraphStyle), 'w:basedOn', 'w:val', 'Normal'); + if (!is_null($paragraphStyle)) { + if ($paragraphStyle->getStyleName() != null) { + $xmlWriter->writeElementBlock('w:basedOn', 'w:val', $paragraphStyle->getStyleName()); + } elseif ($paragraphStyle->getBasedOn() != null) { + $xmlWriter->writeElementBlock('w:basedOn', 'w:val', $paragraphStyle->getBasedOn()); + } + } // w:pPr if (!is_null($paragraphStyle)) { @@ -199,7 +232,6 @@ class Styles extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Paragraph $style - * @return void */ private function writeParagraphStyle(XMLWriter $xmlWriter, $styleName, ParagraphStyle $style) { @@ -232,7 +264,6 @@ class Styles extends AbstractPart * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeTableStyle(XMLWriter $xmlWriter, $styleName, TableStyle $style) { diff --git a/src/PhpWord/Writer/Word2007/Part/Theme.php b/src/PhpWord/Writer/Word2007/Part/Theme.php index e9b16bfc..f4ef478e 100644 --- a/src/PhpWord/Writer/Word2007/Part/Theme.php +++ b/src/PhpWord/Writer/Word2007/Part/Theme.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -48,7 +48,6 @@ class Theme extends AbstractPart return $str; } - /** * Write color scheme * diff --git a/src/PhpWord/Writer/Word2007/Part/WebSettings.php b/src/PhpWord/Writer/Word2007/Part/WebSettings.php index ce42063d..46252e87 100644 --- a/src/PhpWord/Writer/Word2007/Part/WebSettings.php +++ b/src/PhpWord/Writer/Word2007/Part/WebSettings.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php index d0ee5a0d..3236cead 100644 --- a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -71,7 +71,7 @@ abstract class AbstractStyle /** * Get Style * - * @return \PhpOffice\PhpWord\Style\AbstractStyle + * @return string|\PhpOffice\PhpWord\Style\AbstractStyle */ protected function getStyle() { @@ -88,11 +88,11 @@ abstract class AbstractStyle protected function convertTwip($value, $default = 0) { $factors = array( - Settings::UNIT_CM => 567, - Settings::UNIT_MM => 56.7, - Settings::UNIT_INCH => 1440, + Settings::UNIT_CM => 567, + Settings::UNIT_MM => 56.7, + Settings::UNIT_INCH => 1440, Settings::UNIT_POINT => 20, - Settings::UNIT_PICA => 240, + Settings::UNIT_PICA => 240, ); $unit = Settings::getMeasurementUnit(); $factor = 1; @@ -109,12 +109,11 @@ abstract class AbstractStyle * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param string $name * @param mixed $value - * @return void */ protected function writeChildStyle(XMLWriter $xmlWriter, $name, $value) { if ($value !== null) { - $class = "PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\" . $name; + $class = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\' . $name; /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $writer */ $writer = new $class($xmlWriter, $value); diff --git a/src/PhpWord/Writer/Word2007/Style/Cell.php b/src/PhpWord/Writer/Word2007/Style/Cell.php index c9156de1..733b7b43 100644 --- a/src/PhpWord/Writer/Word2007/Style/Cell.php +++ b/src/PhpWord/Writer/Word2007/Style/Cell.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,8 +33,6 @@ class Cell extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -47,10 +45,14 @@ class Cell extends AbstractStyle $xmlWriter->startElement('w:tcPr'); // Width - $xmlWriter->startElement('w:tcW'); - $xmlWriter->writeAttribute('w:w', $this->width); - $xmlWriter->writeAttribute('w:type', 'dxa'); - $xmlWriter->endElement(); // w:tcW + if (!is_null($this->width) || !is_null($style->getWidth())) { + $width = is_null($this->width) ? $style->getWidth() : $this->width; + + $xmlWriter->startElement('w:tcW'); + $xmlWriter->writeAttribute('w:w', $width); + $xmlWriter->writeAttribute('w:type', $style->getUnit()); + $xmlWriter->endElement(); // w:tcW + } // Text direction $textDir = $style->getTextDirection(); @@ -67,6 +69,7 @@ class Cell extends AbstractStyle $styleWriter = new MarginBorder($xmlWriter); $styleWriter->setSizes($style->getBorderSize()); $styleWriter->setColors($style->getBorderColor()); + $styleWriter->setStyles($style->getBorderStyle()); $styleWriter->setAttributes(array('defaultColor' => CellStyle::DEFAULT_BORDER_COLOR)); $styleWriter->write(); @@ -93,7 +96,6 @@ class Cell extends AbstractStyle * Set width. * * @param int $value - * @return void */ public function setWidth($value = null) { diff --git a/src/PhpWord/Writer/Word2007/Style/Extrusion.php b/src/PhpWord/Writer/Word2007/Style/Extrusion.php index 3ecd76e4..19399348 100644 --- a/src/PhpWord/Writer/Word2007/Style/Extrusion.php +++ b/src/PhpWord/Writer/Word2007/Style/Extrusion.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Extrusion extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -37,7 +35,7 @@ class Extrusion extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $xmlWriter->startElement("o:extrusion"); + $xmlWriter->startElement('o:extrusion'); $xmlWriter->writeAttribute('on', 't'); $xmlWriter->writeAttributeIf($style->getType() !== null, 'type', $style->getType()); $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); diff --git a/src/PhpWord/Writer/Word2007/Style/Fill.php b/src/PhpWord/Writer/Word2007/Style/Fill.php index 7ce68106..53d03974 100644 --- a/src/PhpWord/Writer/Word2007/Style/Fill.php +++ b/src/PhpWord/Writer/Word2007/Style/Fill.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Fill extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Style/Font.php b/src/PhpWord/Writer/Word2007/Style/Font.php index 97cf3088..58282d15 100644 --- a/src/PhpWord/Writer/Word2007/Style/Font.php +++ b/src/PhpWord/Writer/Word2007/Style/Font.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -33,8 +33,6 @@ class Font extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -54,8 +52,6 @@ class Font extends AbstractStyle /** * Write full style. - * - * @return void */ private function writeStyle() { @@ -63,6 +59,7 @@ class Font extends AbstractStyle if (!$style instanceof \PhpOffice\PhpWord\Style\Font) { return; } + $xmlWriter = $this->getXmlWriter(); $xmlWriter->startElement('w:rPr'); @@ -86,6 +83,20 @@ class Font extends AbstractStyle $xmlWriter->endElement(); } + //Language + $language = $style->getLang(); + if ($language != null && ($language->getLatin() !== null || $language->getEastAsia() !== null || $language->getBidirectional() !== null)) { + $xmlWriter->startElement('w:lang'); + $xmlWriter->writeAttributeIf($language->getLatin() !== null, 'w:val', $language->getLatin()); + $xmlWriter->writeAttributeIf($language->getEastAsia() !== null, 'w:eastAsia', $language->getEastAsia()); + $xmlWriter->writeAttributeIf($language->getBidirectional() !== null, 'w:bidi', $language->getBidirectional()); + //if bidi is not set but we are writing RTL, write the latin language in the bidi tag + if ($style->isRTL() && $language->getBidirectional() === null && $language->getLatin() !== null) { + $xmlWriter->writeAttribute('w:bidi', $language->getLatin()); + } + $xmlWriter->endElement(); + } + // Color $color = $style->getColor(); $xmlWriter->writeElementIf($color !== null, 'w:color', 'w:val', $color); @@ -97,6 +108,7 @@ class Font extends AbstractStyle // Bold, italic $xmlWriter->writeElementIf($style->isBold(), 'w:b'); + $xmlWriter->writeElementIf($style->isBold(), 'w:bCs'); $xmlWriter->writeElementIf($style->isItalic(), 'w:i'); $xmlWriter->writeElementIf($style->isItalic(), 'w:iCs'); @@ -123,19 +135,25 @@ class Font extends AbstractStyle $xmlWriter->writeElementIf($style->getSpacing() !== null, 'w:spacing', 'w:val', $style->getSpacing()); $xmlWriter->writeElementIf($style->getKerning() !== null, 'w:kern', 'w:val', $style->getKerning() * 2); + // noProof + $xmlWriter->writeElementIf($style->isNoProof() !== false, 'w:noProof'); + // Background-Color $shading = $style->getShading(); if (!is_null($shading)) { $styleWriter = new Shading($xmlWriter, $shading); $styleWriter->write(); } - + // RTL if ($this->isInline === true) { $styleName = $style->getStyleName(); $xmlWriter->writeElementIf($styleName === null && $style->isRTL(), 'w:rtl'); } + // Position + $xmlWriter->writeElementIf($style->getPosition() !== null, 'w:position', 'w:val', $style->getPosition()); + $xmlWriter->endElement(); } @@ -143,7 +161,6 @@ class Font extends AbstractStyle * Set is inline. * * @param bool $value - * @return void */ public function setIsInline($value) { diff --git a/src/PhpWord/Writer/Word2007/Style/Frame.php b/src/PhpWord/Writer/Word2007/Style/Frame.php index 9c6ddaef..10e5b151 100644 --- a/src/PhpWord/Writer/Word2007/Style/Frame.php +++ b/src/PhpWord/Writer/Word2007/Style/Frame.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,10 +28,10 @@ use PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment; */ class Frame extends AbstractStyle { + const PHP_32BIT_INT_MAX = 2147483647; + /** * Write style. - * - * @return void */ public function write() { @@ -41,13 +41,18 @@ class Frame extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $zIndices = array(FrameStyle::WRAP_INFRONT => PHP_INT_MAX, FrameStyle::WRAP_BEHIND => -PHP_INT_MAX); + $maxZIndex = min(PHP_INT_MAX, self::PHP_32BIT_INT_MAX); + $zIndices = array(FrameStyle::WRAP_INFRONT => $maxZIndex, FrameStyle::WRAP_BEHIND => -$maxZIndex); $properties = array( - 'width' => 'width', - 'height' => 'height', - 'left' => 'margin-left', - 'top' => 'margin-top', + 'width' => 'width', + 'height' => 'height', + 'left' => 'margin-left', + 'top' => 'margin-top', + 'wrapDistanceTop' => 'mso-wrap-distance-top', + 'wrapDistanceBottom' => 'mso-wrap-distance-bottom', + 'wrapDistanceLeft' => 'mso-wrap-distance-left', + 'wrapDistanceRight' => 'mso-wrap-distance-right', ); $sizeStyles = $this->getStyles($style, $properties, $style->getUnit()); @@ -62,7 +67,7 @@ class Frame extends AbstractStyle $styles = array_merge($sizeStyles, $posStyles); - // zIndex for infront & behind wrap + // zIndex for infront & behind wrap $wrap = $style->getWrap(); if ($wrap !== null && isset($zIndices[$wrap])) { $styles['z-index'] = $zIndices[$wrap]; @@ -77,8 +82,6 @@ class Frame extends AbstractStyle /** * Write alignment. - * - * @return void */ public function writeAlignment() { @@ -108,7 +111,6 @@ class Frame extends AbstractStyle * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Frame $style * @param string $wrap - * @return void */ private function writeWrap(XMLWriter $xmlWriter, FrameStyle $style, $wrap) { @@ -129,8 +131,8 @@ class Frame extends AbstractStyle $vPos = $style->getVPosRelTo(); if ($pos == FrameStyle::POS_ABSOLUTE) { - $xmlWriter->writeAttribute('anchorx', "page"); - $xmlWriter->writeAttribute('anchory', "page"); + $xmlWriter->writeAttribute('anchorx', 'page'); + $xmlWriter->writeAttribute('anchory', 'page'); } elseif ($pos == FrameStyle::POS_RELATIVE) { if (isset($relativePositions[$hPos])) { $xmlWriter->writeAttribute('anchorx', $relativePositions[$hPos]); diff --git a/src/PhpWord/Writer/Word2007/Style/Image.php b/src/PhpWord/Writer/Word2007/Style/Image.php index 3bbe751e..ef23ed10 100644 --- a/src/PhpWord/Writer/Word2007/Style/Image.php +++ b/src/PhpWord/Writer/Word2007/Style/Image.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/src/PhpWord/Writer/Word2007/Style/Indentation.php b/src/PhpWord/Writer/Word2007/Style/Indentation.php index a7edaee1..961e770f 100644 --- a/src/PhpWord/Writer/Word2007/Style/Indentation.php +++ b/src/PhpWord/Writer/Word2007/Style/Indentation.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Indentation extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Style/Line.php b/src/PhpWord/Writer/Word2007/Style/Line.php index 3407c252..154a42c1 100644 --- a/src/PhpWord/Writer/Word2007/Style/Line.php +++ b/src/PhpWord/Writer/Word2007/Style/Line.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,14 +21,11 @@ use PhpOffice\PhpWord\Style\Line as LineStyle; /** * Line style writer - * */ class Line extends Frame { /** * Write Line stroke. - * - * @return void * @todo Merge with `Stroke` style */ public function writeStroke() diff --git a/src/PhpWord/Writer/Word2007/Style/LineNumbering.php b/src/PhpWord/Writer/Word2007/Style/LineNumbering.php index 592fb7bb..4bf08b65 100644 --- a/src/PhpWord/Writer/Word2007/Style/LineNumbering.php +++ b/src/PhpWord/Writer/Word2007/Style/LineNumbering.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,9 +26,6 @@ class LineNumbering extends AbstractStyle { /** * Write style. - * - * @return void - * * The w:start seems to be zero based so we have to decrement by one */ public function write() diff --git a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php index 68ba70d2..f5c4b015 100644 --- a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php +++ b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,7 +29,7 @@ class MarginBorder extends AbstractStyle /** * Sizes * - * @var integer[] + * @var int[] */ private $sizes = array(); @@ -40,6 +40,13 @@ class MarginBorder extends AbstractStyle */ private $colors = array(); + /** + * Border styles + * + * @var string[] + */ + private $styles = array(); + /** * Other attributes * @@ -49,8 +56,6 @@ class MarginBorder extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -64,7 +69,8 @@ class MarginBorder extends AbstractStyle if (isset($this->colors[$i])) { $color = $this->colors[$i]; } - $this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color); + $style = isset($this->styles[$i]) ? $this->styles[$i] : 'single'; + $this->writeSide($xmlWriter, $sides[$i], $this->sizes[$i], $color, $style); } } } @@ -76,9 +82,9 @@ class MarginBorder extends AbstractStyle * @param string $side * @param int $width * @param string $color - * @return void + * @param string $borderStyle */ - private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null) + private function writeSide(XMLWriter $xmlWriter, $side, $width, $color = null, $borderStyle = 'solid') { $xmlWriter->startElement('w:' . $side); if (!empty($this->colors)) { @@ -87,9 +93,9 @@ class MarginBorder extends AbstractStyle $color = $this->attributes['defaultColor']; } } - $xmlWriter->writeAttribute('w:val', 'single'); + $xmlWriter->writeAttribute('w:val', $borderStyle); $xmlWriter->writeAttribute('w:sz', $width); - $xmlWriter->writeAttribute('w:color', $color); + $xmlWriter->writeAttributeIf($color != null, 'w:color', $color); if (!empty($this->attributes)) { if (isset($this->attributes['space'])) { $xmlWriter->writeAttribute('w:space', $this->attributes['space']); @@ -105,8 +111,7 @@ class MarginBorder extends AbstractStyle /** * Set sizes. * - * @param integer[] $value - * @return void + * @param int[] $value */ public function setSizes($value) { @@ -117,18 +122,26 @@ class MarginBorder extends AbstractStyle * Set colors. * * @param string[] $value - * @return void */ public function setColors($value) { $this->colors = $value; } + /** + * Set border styles. + * + * @param string[] $value + */ + public function setStyles($value) + { + $this->styles = $value; + } + /** * Set attributes. * * @param array $value - * @return void */ public function setAttributes($value) { diff --git a/src/PhpWord/Writer/Word2007/Style/Outline.php b/src/PhpWord/Writer/Word2007/Style/Outline.php index 620720b3..ae4c1da3 100644 --- a/src/PhpWord/Writer/Word2007/Style/Outline.php +++ b/src/PhpWord/Writer/Word2007/Style/Outline.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Outline extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -37,7 +35,7 @@ class Outline extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $xmlWriter->startElement("v:stroke"); + $xmlWriter->startElement('v:stroke'); $xmlWriter->writeAttribute('on', 't'); $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); $xmlWriter->writeAttributeIf($style->getWeight() !== null, 'weight', $style->getWeight() . $style->getUnit()); diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php index 2cb08bee..67616086 100644 --- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php +++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -45,8 +45,6 @@ class Paragraph extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -70,8 +68,6 @@ class Paragraph extends AbstractStyle /** * Write full style. - * - * @return void */ private function writeStyle() { @@ -107,6 +103,18 @@ class Paragraph extends AbstractStyle $xmlWriter->endElement(); } + //Right to left + $xmlWriter->writeElementIf($styles['bidi'] === true, 'w:bidi'); + + //Paragraph contextualSpacing + $xmlWriter->writeElementIf($styles['contextualSpacing'] === true, 'w:contextualSpacing'); + + //Paragraph textAlignment + $xmlWriter->writeElementIf($styles['textAlignment'] !== null, 'w:textAlignment', 'w:val', $styles['textAlignment']); + + // Hyphenation + $xmlWriter->writeElementIf($styles['suppressAutoHyphens'] === true, 'w:suppressAutoHyphens'); + // Child style: alignment, indentation, spacing, and shading $this->writeChildStyle($xmlWriter, 'Indentation', $styles['indentation']); $this->writeChildStyle($xmlWriter, 'Spacing', $styles['spacing']); @@ -140,12 +148,11 @@ class Paragraph extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Tab[] $tabs - * @return void */ private function writeTabs(XMLWriter $xmlWriter, $tabs) { if (!empty($tabs)) { - $xmlWriter->startElement("w:tabs"); + $xmlWriter->startElement('w:tabs'); foreach ($tabs as $tab) { $styleWriter = new Tab($xmlWriter, $tab); $styleWriter->write(); @@ -159,7 +166,6 @@ class Paragraph extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param array $numbering - * @return void */ private function writeNumbering(XMLWriter $xmlWriter, $numbering) { @@ -188,7 +194,6 @@ class Paragraph extends AbstractStyle * Set without w:pPr. * * @param bool $value - * @return void */ public function setWithoutPPR($value) { @@ -199,7 +204,6 @@ class Paragraph extends AbstractStyle * Set is inline. * * @param bool $value - * @return void */ public function setIsInline($value) { diff --git a/src/PhpWord/Writer/Word2007/Style/Row.php b/src/PhpWord/Writer/Word2007/Style/Row.php index e8b7e1a5..82028d24 100644 --- a/src/PhpWord/Writer/Word2007/Style/Row.php +++ b/src/PhpWord/Writer/Word2007/Style/Row.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -31,8 +31,6 @@ class Row extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -60,7 +58,6 @@ class Row extends AbstractStyle * Set height. * * @param int $value - * @return void */ public function setHeight($value = null) { diff --git a/src/PhpWord/Writer/Word2007/Style/Section.php b/src/PhpWord/Writer/Word2007/Style/Section.php index 60b5d869..af77396d 100644 --- a/src/PhpWord/Writer/Word2007/Style/Section.php +++ b/src/PhpWord/Writer/Word2007/Style/Section.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class Section extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Style/Shading.php b/src/PhpWord/Writer/Word2007/Style/Shading.php index c3594b24..0f9d6ccc 100644 --- a/src/PhpWord/Writer/Word2007/Style/Shading.php +++ b/src/PhpWord/Writer/Word2007/Style/Shading.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Shading extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -38,9 +36,9 @@ class Shading extends AbstractStyle $xmlWriter = $this->getXmlWriter(); $xmlWriter->startElement('w:shd'); - $xmlWriter->writeAttribute('w:val', $style->getPattern()); - $xmlWriter->writeAttribute('w:color', $style->getColor()); - $xmlWriter->writeAttribute('w:fill', $style->getFill()); + $xmlWriter->writeAttributeIf(!is_null($style->getPattern()), 'w:val', $style->getPattern()); + $xmlWriter->writeAttributeIf(!is_null($style->getColor()), 'w:color', $style->getColor()); + $xmlWriter->writeAttributeIf(!is_null($style->getFill()), 'w:fill', $style->getFill()); $xmlWriter->endElement(); } } diff --git a/src/PhpWord/Writer/Word2007/Style/Shadow.php b/src/PhpWord/Writer/Word2007/Style/Shadow.php index 239c161d..7fcb12a9 100644 --- a/src/PhpWord/Writer/Word2007/Style/Shadow.php +++ b/src/PhpWord/Writer/Word2007/Style/Shadow.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Shadow extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -37,7 +35,7 @@ class Shadow extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $xmlWriter->startElement("v:shadow"); + $xmlWriter->startElement('v:shadow'); $xmlWriter->writeAttribute('on', 't'); $xmlWriter->writeAttributeIf($style->getColor() !== null, 'color', $style->getColor()); $xmlWriter->writeAttributeIf($style->getOffset() !== null, 'offset', $style->getOffset()); diff --git a/src/PhpWord/Writer/Word2007/Style/Shape.php b/src/PhpWord/Writer/Word2007/Style/Shape.php index 4ed1469d..2def6842 100644 --- a/src/PhpWord/Writer/Word2007/Style/Shape.php +++ b/src/PhpWord/Writer/Word2007/Style/Shape.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Shape extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { diff --git a/src/PhpWord/Writer/Word2007/Style/Spacing.php b/src/PhpWord/Writer/Word2007/Style/Spacing.php index bd2d06aa..0185cbcc 100644 --- a/src/PhpWord/Writer/Word2007/Style/Spacing.php +++ b/src/PhpWord/Writer/Word2007/Style/Spacing.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Spacing extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -48,7 +46,7 @@ class Spacing extends AbstractStyle $line = $style->getLine(); $xmlWriter->writeAttributeIf(!is_null($line), 'w:line', $line); - $xmlWriter->writeAttributeIf(!is_null($line), 'w:lineRule', $style->getRule()); + $xmlWriter->writeAttributeIf(!is_null($line), 'w:lineRule', $style->getLineRule()); $xmlWriter->endElement(); } diff --git a/src/PhpWord/Writer/Word2007/Style/Tab.php b/src/PhpWord/Writer/Word2007/Style/Tab.php index 9867023f..b41653f6 100644 --- a/src/PhpWord/Writer/Word2007/Style/Tab.php +++ b/src/PhpWord/Writer/Word2007/Style/Tab.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,8 +26,6 @@ class Tab extends AbstractStyle { /** * Write style. - * - * @return void */ public function write() { @@ -37,9 +35,9 @@ class Tab extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $xmlWriter->startElement("w:tab"); - $xmlWriter->writeAttribute("w:val", $style->getType()); - $xmlWriter->writeAttribute("w:leader", $style->getLeader()); + $xmlWriter->startElement('w:tab'); + $xmlWriter->writeAttribute('w:val', $style->getType()); + $xmlWriter->writeAttribute('w:leader', $style->getLeader()); $xmlWriter->writeAttribute('w:pos', $this->convertTwip($style->getPosition())); $xmlWriter->endElement(); } diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php index 570e85bb..7f49be7c 100644 --- a/src/PhpWord/Writer/Word2007/Style/Table.php +++ b/src/PhpWord/Writer/Word2007/Style/Table.php @@ -10,14 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Writer\Word2007\Style; use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\SimpleType\TblWidth; use PhpOffice\PhpWord\Style\Table as TableStyle; use PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment; @@ -35,8 +36,6 @@ class Table extends AbstractStyle /** * Write style. - * - * @return void */ public function write() { @@ -51,7 +50,7 @@ class Table extends AbstractStyle $xmlWriter->writeAttribute('w:val', $style); $xmlWriter->endElement(); if (null !== $this->width) { - $this->writeWidth($xmlWriter, $this->width, 'pct'); + $this->writeTblWidth($xmlWriter, 'w:tblW', TblWidth::PERCENT, $this->width); } $xmlWriter->endElement(); } @@ -62,7 +61,6 @@ class Table extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeStyle(XMLWriter $xmlWriter, TableStyle $style) { @@ -79,7 +77,15 @@ class Table extends AbstractStyle $xmlWriter->endElement(); } - $this->writeWidth($xmlWriter, $style->getWidth(), $style->getUnit()); + $this->writeTblWidth($xmlWriter, 'w:tblW', $style->getUnit(), $style->getWidth()); + $this->writeTblWidth($xmlWriter, 'w:tblCellSpacing', TblWidth::TWIP, $style->getCellSpacing()); + $this->writeIndent($xmlWriter, $style); + $this->writeLayout($xmlWriter, $style->getLayout()); + + // Position + $styleWriter = new TablePosition($xmlWriter, $style->getPosition()); + $styleWriter->write(); + $this->writeMargin($xmlWriter, $style); $this->writeBorder($xmlWriter, $style); @@ -95,19 +101,16 @@ class Table extends AbstractStyle } /** - * Write width. + * Enable/Disable automatic resizing of the table * * @param \PhpOffice\Common\XMLWriter $xmlWriter - * @param int $width - * @param string $unit - * @return void + * @param string $layout autofit / fixed */ - private function writeWidth(XMLWriter $xmlWriter, $width, $unit) + private function writeLayout(XMLWriter $xmlWriter, $layout) { - $xmlWriter->startElement('w:tblW'); - $xmlWriter->writeAttribute('w:w', $width); - $xmlWriter->writeAttribute('w:type', $unit); - $xmlWriter->endElement(); // w:tblW + $xmlWriter->startElement('w:tblLayout'); + $xmlWriter->writeAttribute('w:type', $layout); + $xmlWriter->endElement(); // w:tblLayout } /** @@ -115,7 +118,6 @@ class Table extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeMargin(XMLWriter $xmlWriter, TableStyle $style) { @@ -135,7 +137,6 @@ class Table extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeBorder(XMLWriter $xmlWriter, TableStyle $style) { @@ -151,12 +152,30 @@ class Table extends AbstractStyle } } + /** + * Writes a table width + * + * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param string $elementName + * @param string $unit + * @param int|float $width + */ + private function writeTblWidth(XMLWriter $xmlWriter, $elementName, $unit, $width = null) + { + if (null === $width) { + return; + } + $xmlWriter->startElement($elementName); + $xmlWriter->writeAttributeIf(null !== $width, 'w:w', $width); + $xmlWriter->writeAttribute('w:type', $unit); + $xmlWriter->endElement(); + } + /** * Write row style. * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeFirstRow(XMLWriter $xmlWriter, TableStyle $style) { @@ -176,7 +195,6 @@ class Table extends AbstractStyle * * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style - * @return void */ private function writeShading(XMLWriter $xmlWriter, TableStyle $style) { @@ -194,10 +212,24 @@ class Table extends AbstractStyle * Set width. * * @param int $value - * @return void */ public function setWidth($value = null) { $this->width = $value; } + + /** + * @param XMLWriter $xmlWriter + * @param TableStyle $style + */ + private function writeIndent(XMLWriter $xmlWriter, TableStyle $style) + { + $indent = $style->getIndent(); + + if ($indent === null) { + return; + } + + $this->writeTblWidth($xmlWriter, 'w:tblInd', $indent->getType(), $indent->getValue()); + } } diff --git a/src/PhpWord/Writer/Word2007/Style/TablePosition.php b/src/PhpWord/Writer/Word2007/Style/TablePosition.php new file mode 100644 index 00000000..fa57b93c --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/TablePosition.php @@ -0,0 +1,65 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\TablePosition) { + return; + } + + $values = array(); + $properties = array( + 'leftFromText', + 'rightFromText', + 'topFromText', + 'bottomFromText', + 'vertAnchor', + 'horzAnchor', + 'tblpXSpec', + 'tblpX', + 'tblpYSpec', + 'tblpY', + ); + foreach ($properties as $property) { + $method = 'get' . $property; + if (method_exists($style, $method)) { + $values[$property] = $style->$method(); + } + } + $values = array_filter($values); + + if ($values) { + $xmlWriter = $this->getXmlWriter(); + $xmlWriter->startElement('w:tblpPr'); + foreach ($values as $property => $value) { + $xmlWriter->writeAttribute('w:' . $property, $value); + } + $xmlWriter->endElement(); + } + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/TextBox.php b/src/PhpWord/Writer/Word2007/Style/TextBox.php index f8f94da3..627d0c86 100644 --- a/src/PhpWord/Writer/Word2007/Style/TextBox.php +++ b/src/PhpWord/Writer/Word2007/Style/TextBox.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -28,8 +28,6 @@ class TextBox extends Frame { /** * Writer inner margin. - * - * @return void */ public function writeInnerMargin() { @@ -46,8 +44,6 @@ class TextBox extends Frame /** * Writer border. - * - * @return void */ public function writeBorder() { diff --git a/src/PhpWord/Writer/WriterInterface.php b/src/PhpWord/Writer/WriterInterface.php index 1ccdf321..499cde3b 100644 --- a/src/PhpWord/Writer/WriterInterface.php +++ b/src/PhpWord/Writer/WriterInterface.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ diff --git a/tests/PhpWord/Collection/CollectionTest.php b/tests/PhpWord/Collection/CollectionTest.php index 4b2fa0ca..aba63212 100644 --- a/tests/PhpWord/Collection/CollectionTest.php +++ b/tests/PhpWord/Collection/CollectionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Element\Footnote; * * Using concrete class Footnotes instead of AbstractCollection */ -class CollectionTest extends \PHPUnit_Framework_TestCase +class CollectionTest extends \PHPUnit\Framework\TestCase { /** * Test collection diff --git a/tests/PhpWord/ComplexType/FootnotePropertiesTest.php b/tests/PhpWord/ComplexType/FootnotePropertiesTest.php new file mode 100644 index 00000000..4448daf8 --- /dev/null +++ b/tests/PhpWord/ComplexType/FootnotePropertiesTest.php @@ -0,0 +1,79 @@ +setPos(FootnoteProperties::POSITION_DOC_END); + $footnoteProp->setNumFmt(NumberFormat::LOWER_ROMAN); + $footnoteProp->setNumStart(2); + $footnoteProp->setNumRestart(FootnoteProperties::RESTART_NUMBER_EACH_PAGE); + + $this->assertEquals(FootnoteProperties::POSITION_DOC_END, $footnoteProp->getPos()); + $this->assertEquals(NumberFormat::LOWER_ROMAN, $footnoteProp->getNumFmt()); + $this->assertEquals(2, $footnoteProp->getNumStart()); + $this->assertEquals(FootnoteProperties::RESTART_NUMBER_EACH_PAGE, $footnoteProp->getNumRestart()); + } + + /** + * Test throws exception if wrong position given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongPos() + { + $footnoteProp = new FootnoteProperties(); + $footnoteProp->setPos(NumberFormat::LOWER_ROMAN); + } + + /** + * Test throws exception if wrong number format given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongNumFmt() + { + $footnoteProp = new FootnoteProperties(); + $footnoteProp->setNumFmt(FootnoteProperties::POSITION_DOC_END); + } + + /** + * Test throws exception if wrong number restart given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongNumRestart() + { + $footnoteProp = new FootnoteProperties(); + $footnoteProp->setNumRestart(NumberFormat::LOWER_ROMAN); + } +} diff --git a/tests/PhpWord/ComplexType/ProofStateTest.php b/tests/PhpWord/ComplexType/ProofStateTest.php new file mode 100644 index 00000000..cd1e77f7 --- /dev/null +++ b/tests/PhpWord/ComplexType/ProofStateTest.php @@ -0,0 +1,61 @@ +setGrammar(ProofState::CLEAN); + $pState->setSpelling(ProofState::DIRTY); + + $this->assertEquals(ProofState::CLEAN, $pState->getGrammar()); + $this->assertEquals(ProofState::DIRTY, $pState->getSpelling()); + } + + /** + * Test throws exception if wrong grammar proof state value given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongGrammar() + { + $pState = new ProofState(); + $pState->setGrammar('Wrong'); + } + + /** + * Test throws exception if wrong spelling proof state value given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongSpelling() + { + $pState = new ProofState(); + $pState->setSpelling('Wrong'); + } +} diff --git a/tests/PhpWord/Element/AbstractElementTest.php b/tests/PhpWord/Element/AbstractElementTest.php index 83f209e4..f0531b34 100644 --- a/tests/PhpWord/Element/AbstractElementTest.php +++ b/tests/PhpWord/Element/AbstractElementTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Element; /** * Test class for PhpOffice\PhpWord\Element\AbstractElement */ -class AbstractElementTest extends \PHPUnit_Framework_TestCase +class AbstractElementTest extends \PHPUnit\Framework\TestCase { /** * Test set/get element index diff --git a/tests/PhpWord/Element/BookmarkTest.php b/tests/PhpWord/Element/BookmarkTest.php new file mode 100644 index 00000000..04e3f6d5 --- /dev/null +++ b/tests/PhpWord/Element/BookmarkTest.php @@ -0,0 +1,38 @@ +assertInstanceOf('PhpOffice\\PhpWord\\Element\\Bookmark', $oBookmark); + $this->assertEquals($bookmarkName, $oBookmark->getName()); + } +} diff --git a/tests/PhpWord/Element/CellTest.php b/tests/PhpWord/Element/CellTest.php index f1d6a280..d4aaa488 100644 --- a/tests/PhpWord/Element/CellTest.php +++ b/tests/PhpWord/Element/CellTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class CellTest extends \PHPUnit_Framework_TestCase +class CellTest extends \PHPUnit\Framework\TestCase { /** * New instance @@ -181,7 +181,7 @@ class CellTest extends \PHPUnit_Framework_TestCase $element = $oCell->addObject($src); $this->assertCount(1, $oCell->getElements()); - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $element); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $element); } /** diff --git a/tests/PhpWord/Element/CheckBoxTest.php b/tests/PhpWord/Element/CheckBoxTest.php index 183d22db..f732407b 100644 --- a/tests/PhpWord/Element/CheckBoxTest.php +++ b/tests/PhpWord/Element/CheckBoxTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\Style\Font; * * @runTestsInSeparateProcesses */ -class CheckBoxTest extends \PHPUnit_Framework_TestCase +class CheckBoxTest extends \PHPUnit\Framework\TestCase { /** * Construct diff --git a/tests/PhpWord/Element/CommentTest.php b/tests/PhpWord/Element/CommentTest.php new file mode 100644 index 00000000..b9c3dfce --- /dev/null +++ b/tests/PhpWord/Element/CommentTest.php @@ -0,0 +1,103 @@ +setStartElement($oText); + $oComment->setEndElement($oText); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Comment', $oComment); + $this->assertEquals($author, $oComment->getAuthor()); + $this->assertEquals($date, $oComment->getDate()); + $this->assertEquals($initials, $oComment->getInitials()); + $this->assertEquals($oText, $oComment->getStartElement()); + $this->assertEquals($oText, $oComment->getEndElement()); + } + + /** + * Add text + */ + public function testAddText() + { + $oComment = new Comment('Test User', new \DateTime(), 'my_initials'); + $element = $oComment->addText('text'); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Text', $element); + $this->assertCount(1, $oComment->getElements()); + $this->assertEquals('text', $element->getText()); + } + + /** + * Get elements + */ + public function testGetElements() + { + $oComment = new Comment('Test User', new \DateTime(), 'my_initials'); + + $this->assertInternalType('array', $oComment->getElements()); + } + + /** + * Set/get relation Id + */ + public function testRelationId() + { + $oComment = new Comment('Test User', new \DateTime(), 'my_initials'); + + $iVal = rand(1, 1000); + $oComment->setRelationId($iVal); + $this->assertEquals($iVal, $oComment->getRelationId()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testExceptionOnCommentStartOnComment() + { + $dummyComment = new Comment('Test User', new \DateTime(), 'my_initials'); + $oComment = new Comment('Test User', new \DateTime(), 'my_initials'); + $oComment->setCommentRangeStart($dummyComment); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testExceptionOnCommentEndOnComment() + { + $dummyComment = new Comment('Test User', new \DateTime(), 'my_initials'); + $oComment = new Comment('Test User', new \DateTime(), 'my_initials'); + $oComment->setCommentRangeEnd($dummyComment); + } +} diff --git a/tests/PhpWord/Element/FieldTest.php b/tests/PhpWord/Element/FieldTest.php index b9afad1f..1c1c0ca1 100644 --- a/tests/PhpWord/Element/FieldTest.php +++ b/tests/PhpWord/Element/FieldTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class FieldTest extends \PHPUnit_Framework_TestCase +class FieldTest extends \PHPUnit\Framework\TestCase { /** * New instance @@ -70,6 +70,47 @@ class FieldTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('SakaEraCalendar', 'PreserveFormat'), $oField->getOptions()); } + /** + * New instance with type and properties and options and text + */ + public function testConstructWithTypePropertiesOptionsText() + { + $oField = new Field('XE', array(), array('Bold', 'Italic'), 'FieldValue'); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Field', $oField); + $this->assertEquals('XE', $oField->getType()); + $this->assertEquals(array(), $oField->getProperties()); + $this->assertEquals(array('Bold', 'Italic'), $oField->getOptions()); + $this->assertEquals('FieldValue', $oField->getText()); + } + + /** + * New instance with type and properties and options and text as TextRun + */ + public function testConstructWithTypePropertiesOptionsTextAsTextRun() + { + $textRun = new TextRun(); + $textRun->addText('test string'); + + $oField = new Field('XE', array(), array('Bold', 'Italic'), $textRun); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Field', $oField); + $this->assertEquals('XE', $oField->getType()); + $this->assertEquals(array(), $oField->getProperties()); + $this->assertEquals(array('Bold', 'Italic'), $oField->getOptions()); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oField->getText()); + } + + public function testConstructWithOptionValue() + { + $oField = new Field('INDEX', array(), array('\\c "3" \\h "A"')); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Field', $oField); + $this->assertEquals('INDEX', $oField->getType()); + $this->assertEquals(array(), $oField->getProperties()); + $this->assertEquals(array('\\c "3" \\h "A"'), $oField->getOptions()); + } + /** * Test setType exception * @@ -105,4 +146,16 @@ class FieldTest extends \PHPUnit_Framework_TestCase $object = new Field('PAGE'); $object->setOptions(array('foo' => 'bar')); } + + /** + * Test setText exception + * + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid text + */ + public function testSetTextException() + { + $object = new Field('XE'); + $object->setText(array()); + } } diff --git a/tests/PhpWord/Element/FooterTest.php b/tests/PhpWord/Element/FooterTest.php index 33a211d3..9de2487a 100644 --- a/tests/PhpWord/Element/FooterTest.php +++ b/tests/PhpWord/Element/FooterTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class FooterTest extends \PHPUnit_Framework_TestCase +class FooterTest extends \PHPUnit\Framework\TestCase { /** * New instance diff --git a/tests/PhpWord/Element/FootnoteTest.php b/tests/PhpWord/Element/FootnoteTest.php index a3f3b4d8..4ea330f5 100644 --- a/tests/PhpWord/Element/FootnoteTest.php +++ b/tests/PhpWord/Element/FootnoteTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class FootnoteTest extends \PHPUnit_Framework_TestCase +class FootnoteTest extends \PHPUnit\Framework\TestCase { /** * New instance without parameter diff --git a/tests/PhpWord/Element/HeaderTest.php b/tests/PhpWord/Element/HeaderTest.php index f75910aa..e61175f1 100644 --- a/tests/PhpWord/Element/HeaderTest.php +++ b/tests/PhpWord/Element/HeaderTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class HeaderTest extends \PHPUnit_Framework_TestCase +class HeaderTest extends \PHPUnit\Framework\TestCase { /** * New instance @@ -228,7 +228,7 @@ class HeaderTest extends \PHPUnit_Framework_TestCase /** * Add footnote exception * - * @expectedException BadMethodCallException + * @expectedException \BadMethodCallException */ public function testAddFootnoteException() { diff --git a/tests/PhpWord/Element/ImageTest.php b/tests/PhpWord/Element/ImageTest.php index 9ef9694b..747a77ac 100644 --- a/tests/PhpWord/Element/ImageTest.php +++ b/tests/PhpWord/Element/ImageTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class ImageTest extends \PHPUnit_Framework_TestCase +class ImageTest extends \PHPUnit\Framework\TestCase { /** * New instance @@ -37,8 +37,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); $this->assertEquals($src, $oImage->getSource()); $this->assertEquals(md5($src), $oImage->getMediaId()); - // todo: change to assertNotTrue when got upgraded to PHPUnit 4.x - $this->assertEquals(false, $oImage->isWatermark()); + $this->assertFalse($oImage->isWatermark()); $this->assertEquals(Image::SOURCE_LOCAL, $oImage->getSourceType()); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oImage->getStyle()); } @@ -87,6 +86,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase $this->assertEquals($createFunction, $image->getImageCreateFunction()); $this->assertEquals($imageFunction, $image->getImageFunction()); $this->assertFalse($image->isMemImage()); + $this->assertNotNull($image->getImageStringData()); } } @@ -131,7 +131,15 @@ class ImageTest extends \PHPUnit_Framework_TestCase */ public function testUnsupportedImage() { - $object = new Image('http://samples.libav.org/image-samples/RACECAR.BMP'); + //disable ssl verification, never do this in real application, you should pass the certiciate instead!!! + $arrContextOptions = array( + 'ssl' => array( + 'verify_peer' => false, + 'verify_peer_name' => false, + ), + ); + stream_context_set_default($arrContextOptions); + $object = new Image('https://samples.libav.org/image-samples/RACECAR.BMP'); $object->getSource(); } @@ -194,9 +202,33 @@ class ImageTest extends \PHPUnit_Framework_TestCase $this->assertEquals(md5($source), $image->getMediaId()); $this->assertEquals('image/jpeg', $image->getImageType()); $this->assertEquals('jpg', $image->getImageExtension()); - $this->assertEquals('imagecreatefromjpeg', $image->getImageCreateFunction()); + $this->assertEquals('imagecreatefromstring', $image->getImageCreateFunction()); $this->assertEquals('imagejpeg', $image->getImageFunction()); $this->assertTrue($image->isMemImage()); + + $this->assertNotNull($image->getImageStringData()); + $this->assertNotNull($image->getImageStringData(true)); + } + + /** + * Test construct from GD + */ + public function testConstructFromGd() + { + $source = 'http://php.net/images/logos/php-icon.png'; + + $image = new Image($source); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image); + $this->assertEquals($source, $image->getSource()); + $this->assertEquals(md5($source), $image->getMediaId()); + $this->assertEquals('image/png', $image->getImageType()); + $this->assertEquals('png', $image->getImageExtension()); + $this->assertEquals('imagecreatefrompng', $image->getImageCreateFunction()); + $this->assertEquals('imagepng', $image->getImageFunction()); + $this->assertTrue($image->isMemImage()); + + $this->assertNotNull($image->getImageStringData()); + $this->assertNotNull($image->getImageStringData(true)); } /** diff --git a/tests/PhpWord/Element/LineTest.php b/tests/PhpWord/Element/LineTest.php index a7b15b08..20eee74f 100644 --- a/tests/PhpWord/Element/LineTest.php +++ b/tests/PhpWord/Element/LineTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\Line * @runTestsInSeparateProcesses */ -class LineTest extends \PHPUnit_Framework_TestCase +class LineTest extends \PHPUnit\Framework\TestCase { /** * Create new instance diff --git a/tests/PhpWord/Element/LinkTest.php b/tests/PhpWord/Element/LinkTest.php index 40f07a1f..e1be7521 100644 --- a/tests/PhpWord/Element/LinkTest.php +++ b/tests/PhpWord/Element/LinkTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\Style\Font; * @coversDefaultClass \PhpOffice\PhpWord\Element\Link * @runTestsInSeparateProcesses */ -class LinkTest extends \PHPUnit_Framework_TestCase +class LinkTest extends \PHPUnit\Framework\TestCase { /** * Create new instance @@ -49,7 +49,7 @@ class LinkTest extends \PHPUnit_Framework_TestCase $oLink = new Link( 'https://github.com/PHPOffice/PHPWord', 'PHPWord on GitHub', - array('color' => '0000FF', 'underline' => Font::UNDERLINE_SINGLE), + array('color' => '0000FF', 'underline' => Font::UNDERLINE_SINGLE), array('marginLeft' => 600, 'marginRight' => 600, 'marginTop' => 600, 'marginBottom' => 600) ); diff --git a/tests/PhpWord/Element/ListItemRunTest.php b/tests/PhpWord/Element/ListItemRunTest.php index 91609357..95eb17eb 100644 --- a/tests/PhpWord/Element/ListItemRunTest.php +++ b/tests/PhpWord/Element/ListItemRunTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,18 +22,18 @@ namespace PhpOffice\PhpWord\Element; * * @runTestsInSeparateProcesses */ -class ListItemRunTest extends \PHPUnit_Framework_TestCase +class ListItemRunTest extends \PHPUnit\Framework\TestCase { /** * New instance */ - public function testConstructNull() + public function testConstruct() { $oListItemRun = new ListItemRun(); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\ListItemRun', $oListItemRun); $this->assertCount(0, $oListItemRun->getElements()); - $this->assertNull($oListItemRun->getParagraphStyle()); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oListItemRun->getParagraphStyle()); } /** diff --git a/tests/PhpWord/Element/ListItemTest.php b/tests/PhpWord/Element/ListItemTest.php index 2dc4f65c..e5c815ec 100644 --- a/tests/PhpWord/Element/ListItemTest.php +++ b/tests/PhpWord/Element/ListItemTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\ListItem * @runTestsInSeparateProcesses */ -class ListItemTest extends \PHPUnit_Framework_TestCase +class ListItemTest extends \PHPUnit\Framework\TestCase { /** * Get text object diff --git a/tests/PhpWord/Element/ObjectTest.php b/tests/PhpWord/Element/ObjectTest.php index 44516b61..9fbe1bb5 100644 --- a/tests/PhpWord/Element/ObjectTest.php +++ b/tests/PhpWord/Element/ObjectTest.php @@ -10,30 +10,43 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Element; /** - * Test class for PhpOffice\PhpWord\Element\Object + * Test class for PhpOffice\PhpWord\Element\OLEObject * - * @coversDefaultClass \PhpOffice\PhpWord\Element\Object + * @coversDefaultClass \PhpOffice\PhpWord\Element\OLEObject * @runTestsInSeparateProcesses */ -class ObjectTest extends \PHPUnit_Framework_TestCase +class ObjectTest extends \PHPUnit\Framework\TestCase { /** - * Create new instance with supported files + * Create new instance with supported files, 4 character extention */ public function testConstructWithSupportedFiles() { - $src = __DIR__ . '/../_files/documents/sheet.xls'; - $oObject = new Object($src); + $src = __DIR__ . '/../_files/documents/reader.docx'; + $oObject = new OLEObject($src); - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $oObject); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle()); + $this->assertEquals($src, $oObject->getSource()); + } + + /** + * Create new instance with supported files + */ + public function testConstructWithSupportedFilesLong() + { + $src = __DIR__ . '/../_files/documents/sheet.xls'; + $oObject = new OLEObject($src); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle()); $this->assertEquals($src, $oObject->getSource()); } @@ -46,7 +59,7 @@ class ObjectTest extends \PHPUnit_Framework_TestCase public function testConstructWithNotSupportedFiles() { $src = __DIR__ . '/../_files/xsl/passthrough.xsl'; - $oObject = new Object($src); + $oObject = new OLEObject($src); $oObject->getSource(); } @@ -56,9 +69,9 @@ class ObjectTest extends \PHPUnit_Framework_TestCase public function testConstructWithSupportedFilesAndStyle() { $src = __DIR__ . '/../_files/documents/sheet.xls'; - $oObject = new Object($src, array('width' => '230px')); + $oObject = new OLEObject($src, array('width' => '230px')); - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Object', $oObject); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\OLEObject', $oObject); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oObject->getStyle()); $this->assertEquals($src, $oObject->getSource()); } @@ -69,7 +82,7 @@ class ObjectTest extends \PHPUnit_Framework_TestCase public function testRelationId() { $src = __DIR__ . '/../_files/documents/sheet.xls'; - $oObject = new Object($src); + $oObject = new OLEObject($src); $iVal = rand(1, 1000); $oObject->setRelationId($iVal); @@ -82,7 +95,7 @@ class ObjectTest extends \PHPUnit_Framework_TestCase public function testImageRelationId() { $src = __DIR__ . '/../_files/documents/sheet.xls'; - $oObject = new Object($src); + $oObject = new OLEObject($src); $iVal = rand(1, 1000); $oObject->setImageRelationId($iVal); diff --git a/tests/PhpWord/Element/PageBreakTest.php b/tests/PhpWord/Element/PageBreakTest.php index 3d8b1db6..d4491fe1 100644 --- a/tests/PhpWord/Element/PageBreakTest.php +++ b/tests/PhpWord/Element/PageBreakTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\PageBreak * @runTestsInSeparateProcesses */ -class PageBreakTest extends \PHPUnit_Framework_TestCase +class PageBreakTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class diff --git a/tests/PhpWord/Element/PreserveTextTest.php b/tests/PhpWord/Element/PreserveTextTest.php index 33e2272a..97e49b93 100644 --- a/tests/PhpWord/Element/PreserveTextTest.php +++ b/tests/PhpWord/Element/PreserveTextTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class PreserveTextTest extends \PHPUnit_Framework_TestCase +class PreserveTextTest extends \PHPUnit\Framework\TestCase { /** * Create new instance diff --git a/tests/PhpWord/Element/RowTest.php b/tests/PhpWord/Element/RowTest.php index 58a166f4..3c534502 100644 --- a/tests/PhpWord/Element/RowTest.php +++ b/tests/PhpWord/Element/RowTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\Row * @runTestsInSeparateProcesses */ -class RowTest extends \PHPUnit_Framework_TestCase +class RowTest extends \PHPUnit\Framework\TestCase { /** * Create new instance diff --git a/tests/PhpWord/Element/SDTTest.php b/tests/PhpWord/Element/SDTTest.php index 52705bc1..6e40bae0 100644 --- a/tests/PhpWord/Element/SDTTest.php +++ b/tests/PhpWord/Element/SDTTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Element; * * @coversDefaultClass \PhpOffice\PhpWord\Element\SDT */ -class SDTTest extends \PHPUnit_Framework_TestCase +class SDTTest extends \PHPUnit\Framework\TestCase { /** * Create new instance @@ -32,14 +32,20 @@ class SDTTest extends \PHPUnit_Framework_TestCase $types = array('comboBox', 'dropDownList', 'date'); $type = $types[rand(0, 2)]; $value = rand(0, 100); + $alias = 'alias'; + $tag = 'my_tag'; $object = new SDT($type); $object->setValue($value); $object->setListItems($types); + $object->setAlias($alias); + $object->setTag($tag); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\SDT', $object); $this->assertEquals($type, $object->getType()); $this->assertEquals($types, $object->getListItems()); $this->assertEquals($value, $object->getValue()); + $this->assertEquals($alias, $object->getAlias()); + $this->assertEquals($tag, $object->getTag()); } /** diff --git a/tests/PhpWord/Element/SectionTest.php b/tests/PhpWord/Element/SectionTest.php index 78010bc9..265307d7 100644 --- a/tests/PhpWord/Element/SectionTest.php +++ b/tests/PhpWord/Element/SectionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Element\Section * @runTestsInSeparateProcesses */ -class SectionTest extends \PHPUnit_Framework_TestCase +class SectionTest extends \PHPUnit\Framework\TestCase { /** * @covers ::setStyle @@ -70,7 +70,7 @@ class SectionTest extends \PHPUnit_Framework_TestCase 'PageBreak', 'Table', 'ListItem', - 'Object', + 'OLEObject', 'Image', 'Title', 'TextRun', @@ -129,6 +129,17 @@ class SectionTest extends \PHPUnit_Framework_TestCase $this->assertFalse($object->hasDifferentFirstPage()); } + /** + * @covers ::addHeader + * @covers ::hasDifferentFirstPage + */ + public function testHasDifferentFirstPageFooter() + { + $object = new Section(1); + $object->addFooter(Header::FIRST); + $this->assertTrue($object->hasDifferentFirstPage()); + } + /** * @covers ::addHeader * @covers ::hasDifferentFirstPage @@ -151,4 +162,35 @@ class SectionTest extends \PHPUnit_Framework_TestCase $object = new Section(1); $object->addHeader('ODD'); } + + /** + * @covers \PhpOffice\PhpWord\Element\AbstractContainer::removeElement + */ + public function testRemoveElementByIndex() + { + $section = new Section(1); + $section->addText('firstText'); + $section->addText('secondText'); + + $this->assertEquals(2, $section->countElements()); + $section->removeElement(1); + + $this->assertEquals(1, $section->countElements()); + } + + /** + * @covers \PhpOffice\PhpWord\Element\AbstractContainer::removeElement + */ + public function testRemoveElementByElement() + { + $section = new Section(1); + $firstText = $section->addText('firstText'); + $secondText = $section->addText('secondText'); + + $this->assertEquals(2, $section->countElements()); + $section->removeElement($firstText); + + $this->assertEquals(1, $section->countElements()); + $this->assertEquals($secondText->getElementId(), $section->getElement(1)->getElementId()); + } } diff --git a/tests/PhpWord/Element/TOCTest.php b/tests/PhpWord/Element/TOCTest.php index 6b5867bc..5f5f518f 100644 --- a/tests/PhpWord/Element/TOCTest.php +++ b/tests/PhpWord/Element/TOCTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\PhpWord; * * @runTestsInSeparateProcesses */ -class TOCTest extends \PHPUnit_Framework_TestCase +class TOCTest extends \PHPUnit\Framework\TestCase { /** * Construct with font and TOC style in array format diff --git a/tests/PhpWord/Element/TableTest.php b/tests/PhpWord/Element/TableTest.php index 785ec40a..8ae5306c 100644 --- a/tests/PhpWord/Element/TableTest.php +++ b/tests/PhpWord/Element/TableTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\Table * @runTestsInSeparateProcesses */ -class TableTest extends \PHPUnit_Framework_TestCase +class TableTest extends \PHPUnit\Framework\TestCase { /** * Create new instance diff --git a/tests/PhpWord/Element/TextBoxTest.php b/tests/PhpWord/Element/TextBoxTest.php index cb3fdb99..cd50acd4 100644 --- a/tests/PhpWord/Element/TextBoxTest.php +++ b/tests/PhpWord/Element/TextBoxTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\TextBox * @runTestsInSeparateProcesses */ -class TextBoxTest extends \PHPUnit_Framework_TestCase +class TextBoxTest extends \PHPUnit\Framework\TestCase { /** * Create new instance diff --git a/tests/PhpWord/Element/TextBreakTest.php b/tests/PhpWord/Element/TextBreakTest.php index 40ed6965..13084c67 100644 --- a/tests/PhpWord/Element/TextBreakTest.php +++ b/tests/PhpWord/Element/TextBreakTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,7 +26,7 @@ use PhpOffice\PhpWord\Style\Paragraph; * @coversDefaultClass \PhpOffice\PhpWord\Element\TextBreak * @runTestsInSeparateProcesses */ -class TextBreakTest extends \PHPUnit_Framework_TestCase +class TextBreakTest extends \PHPUnit\Framework\TestCase { /** * Construct with empty value diff --git a/tests/PhpWord/Element/TextRunTest.php b/tests/PhpWord/Element/TextRunTest.php index efd8d6f3..2168bcc4 100644 --- a/tests/PhpWord/Element/TextRunTest.php +++ b/tests/PhpWord/Element/TextRunTest.php @@ -10,32 +10,34 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Element; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\Style\Paragraph; /** * Test class for PhpOffice\PhpWord\Element\TextRun * * @runTestsInSeparateProcesses */ -class TextRunTest extends \PHPUnit_Framework_TestCase +class TextRunTest extends \PHPUnit\Framework\TestCase { /** * New instance */ - public function testConstructNull() + public function testConstruct() { $oTextRun = new TextRun(); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTextRun); $this->assertCount(0, $oTextRun->getElements()); - $this->assertNull($oTextRun->getParagraphStyle()); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle()); } /** @@ -62,6 +64,21 @@ class TextRunTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle()); } + /** + * New instance with object + */ + public function testConstructObject() + { + $oParagraphStyle = new Paragraph(); + $oParagraphStyle->setAlignment(Jc::BOTH); + $oTextRun = new TextRun($oParagraphStyle); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTextRun); + $this->assertCount(0, $oTextRun->getElements()); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oTextRun->getParagraphStyle()); + $this->assertEquals(Jc::BOTH, $oTextRun->getParagraphStyle()->getAlignment()); + } + /** * Add text */ @@ -152,4 +169,16 @@ class TextRunTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Footnote', $element); $this->assertCount(1, $oTextRun->getElements()); } + + /** + * Get paragraph style + */ + public function testParagraph() + { + $oText = new TextRun('paragraphStyle'); + $this->assertEquals('paragraphStyle', $oText->getParagraphStyle()); + + $oText->setParagraphStyle(array('alignment' => Jc::CENTER, 'spaceAfter' => 100)); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $oText->getParagraphStyle()); + } } diff --git a/tests/PhpWord/Element/TextTest.php b/tests/PhpWord/Element/TextTest.php index d2fe0472..97be7ae5 100644 --- a/tests/PhpWord/Element/TextTest.php +++ b/tests/PhpWord/Element/TextTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\Style\Font; * * @runTestsInSeparateProcesses */ -class TextTest extends \PHPUnit_Framework_TestCase +class TextTest extends \PHPUnit\Framework\TestCase { /** * New instance diff --git a/tests/PhpWord/Element/TitleTest.php b/tests/PhpWord/Element/TitleTest.php index 2b886e5e..6ef87c3e 100644 --- a/tests/PhpWord/Element/TitleTest.php +++ b/tests/PhpWord/Element/TitleTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Element; * @coversDefaultClass \PhpOffice\PhpWord\Element\Title * @runTestsInSeparateProcesses */ -class TitleTest extends \PHPUnit_Framework_TestCase +class TitleTest extends \PHPUnit\Framework\TestCase { /** * Create new instance @@ -45,4 +45,25 @@ class TitleTest extends \PHPUnit_Framework_TestCase $this->assertNull($oTitle->getStyle()); } + + /** + * Create new instance with TextRun + */ + public function testConstructWithTextRun() + { + $oTextRun = new TextRun(); + $oTextRun->addText('text'); + $oTitle = new Title($oTextRun); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTitle->getText()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructWithInvalidArgument() + { + $oPageBreak = new PageBreak(); + new Title($oPageBreak); + } } diff --git a/tests/PhpWord/Element/TrackChangeTest.php b/tests/PhpWord/Element/TrackChangeTest.php new file mode 100644 index 00000000..df86feb2 --- /dev/null +++ b/tests/PhpWord/Element/TrackChangeTest.php @@ -0,0 +1,44 @@ +setTrackChange($oTrackChange); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TrackChange', $oTrackChange); + $this->assertEquals($author, $oTrackChange->getAuthor()); + $this->assertEquals($date, $oTrackChange->getDate()); + $this->assertEquals(TrackChange::INSERTED, $oTrackChange->getChangeType()); + } +} diff --git a/tests/PhpWord/Exception/CopyFileExceptionTest.php b/tests/PhpWord/Exception/CopyFileExceptionTest.php index 0bc2e322..5fed9c9f 100644 --- a/tests/PhpWord/Exception/CopyFileExceptionTest.php +++ b/tests/PhpWord/Exception/CopyFileExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,7 +21,7 @@ namespace PhpOffice\PhpWord\Exception; * @covers \PhpOffice\PhpWord\Exception\CopyFileException * @coversDefaultClass \PhpOffice\PhpWord\Exception\CopyFileException */ -class CopyFileExceptionTest extends \PHPUnit_Framework_TestCase +class CopyFileExceptionTest extends \PHPUnit\Framework\TestCase { /** * CopyFileException can be thrown. diff --git a/tests/PhpWord/Exception/CreateTemporaryFileExceptionTest.php b/tests/PhpWord/Exception/CreateTemporaryFileExceptionTest.php index d68bf573..f879285e 100644 --- a/tests/PhpWord/Exception/CreateTemporaryFileExceptionTest.php +++ b/tests/PhpWord/Exception/CreateTemporaryFileExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -21,7 +21,7 @@ namespace PhpOffice\PhpWord\Exception; * @covers \PhpOffice\PhpWord\Exception\CreateTemporaryFileException * @coversDefaultClass \PhpOffice\PhpWord\Exception\CreateTemporaryFileException */ -class CreateTemporaryFileExceptionTest extends \PHPUnit_Framework_TestCase +class CreateTemporaryFileExceptionTest extends \PHPUnit\Framework\TestCase { /** * CreateTemporaryFileException can be thrown. diff --git a/tests/PhpWord/Exception/ExceptionTest.php b/tests/PhpWord/Exception/ExceptionTest.php index 4c14abb9..8c7bce57 100644 --- a/tests/PhpWord/Exception/ExceptionTest.php +++ b/tests/PhpWord/Exception/ExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Exception; * @coversDefaultClass \PhpOffice\PhpWord\Exception\Exception * @runTestsInSeparateProcesses */ -class ExceptionTest extends \PHPUnit_Framework_TestCase +class ExceptionTest extends \PHPUnit\Framework\TestCase { /** * Throw new exception @@ -33,6 +33,6 @@ class ExceptionTest extends \PHPUnit_Framework_TestCase */ public function testThrowException() { - throw new Exception; + throw new Exception(); } } diff --git a/tests/PhpWord/Exception/InvalidImageExceptionTest.php b/tests/PhpWord/Exception/InvalidImageExceptionTest.php index d83aa878..71da1aa9 100644 --- a/tests/PhpWord/Exception/InvalidImageExceptionTest.php +++ b/tests/PhpWord/Exception/InvalidImageExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Exception; * @coversDefaultClass \PhpOffice\PhpWord\Exception\InvalidImageException * @runTestsInSeparateProcesses */ -class InvalidImageExceptionTest extends \PHPUnit_Framework_TestCase +class InvalidImageExceptionTest extends \PHPUnit\Framework\TestCase { /** * Throw new exception @@ -33,6 +33,6 @@ class InvalidImageExceptionTest extends \PHPUnit_Framework_TestCase */ public function testThrowException() { - throw new InvalidImageException; + throw new InvalidImageException(); } } diff --git a/tests/PhpWord/Exception/InvalidStyleExceptionTest.php b/tests/PhpWord/Exception/InvalidStyleExceptionTest.php index 5038ed2f..1d981449 100644 --- a/tests/PhpWord/Exception/InvalidStyleExceptionTest.php +++ b/tests/PhpWord/Exception/InvalidStyleExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Exception; * @coversDefaultClass \PhpOffice\PhpWord\Exception\InvalidStyleException * @runTestsInSeparateProcesses */ -class InvalidStyleExceptionTest extends \PHPUnit_Framework_TestCase +class InvalidStyleExceptionTest extends \PHPUnit\Framework\TestCase { /** * Throw new exception @@ -33,6 +33,6 @@ class InvalidStyleExceptionTest extends \PHPUnit_Framework_TestCase */ public function testThrowException() { - throw new InvalidStyleException; + throw new InvalidStyleException(); } } diff --git a/tests/PhpWord/Exception/UnsupportedImageTypeExceptionTest.php b/tests/PhpWord/Exception/UnsupportedImageTypeExceptionTest.php index 251ed957..5b03f5e3 100644 --- a/tests/PhpWord/Exception/UnsupportedImageTypeExceptionTest.php +++ b/tests/PhpWord/Exception/UnsupportedImageTypeExceptionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Exception; * @coversDefaultClass \PhpOffice\PhpWord\Exception\UnsupportedImageTypeExceptionTest * @runTestsInSeparateProcesses */ -class UnsupportedImageTypeExceptionTest extends \PHPUnit_Framework_TestCase +class UnsupportedImageTypeExceptionTest extends \PHPUnit\Framework\TestCase { /** * Throw new exception @@ -33,6 +33,6 @@ class UnsupportedImageTypeExceptionTest extends \PHPUnit_Framework_TestCase */ public function testThrowException() { - throw new UnsupportedImageTypeException; + throw new UnsupportedImageTypeException(); } } diff --git a/tests/PhpWord/IOFactoryTest.php b/tests/PhpWord/IOFactoryTest.php index 9c2d1e67..4a59e702 100644 --- a/tests/PhpWord/IOFactoryTest.php +++ b/tests/PhpWord/IOFactoryTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord; * * @runTestsInSeparateProcesses */ -class IOFactoryTest extends \PHPUnit_Framework_TestCase +class IOFactoryTest extends \PHPUnit\Framework\TestCase { /** * Create existing writer diff --git a/tests/PhpWord/MediaTest.php b/tests/PhpWord/MediaTest.php index 25480313..02492016 100644 --- a/tests/PhpWord/MediaTest.php +++ b/tests/PhpWord/MediaTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Element\Image; * * @runTestsInSeparateProcesses */ -class MediaTest extends \PHPUnit_Framework_TestCase +class MediaTest extends \PHPUnit\Framework\TestCase { /** * Get section media elements @@ -106,7 +106,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase /** * Add image element exception * - * @expectedException Exception + * @expectedException \Exception * @expectedExceptionMessage Image object not assigned. */ public function testAddElementImageException() diff --git a/tests/PhpWord/Metadata/DocInfoTest.php b/tests/PhpWord/Metadata/DocInfoTest.php index 23572710..25a323d2 100644 --- a/tests/PhpWord/Metadata/DocInfoTest.php +++ b/tests/PhpWord/Metadata/DocInfoTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Metadata; * * @runTestsInSeparateProcesses */ -class DocInfoTest extends \PHPUnit_Framework_TestCase +class DocInfoTest extends \PHPUnit\Framework\TestCase { /** * Creator @@ -193,8 +193,7 @@ class DocInfoTest extends \PHPUnit_Framework_TestCase $this->assertEquals('value5', $oProperties->getCustomPropertyValue('key5')); $this->assertNull($oProperties->getCustomPropertyValue('key6')); $this->assertTrue($oProperties->isCustomPropertySet('key5')); - // todo: change to assertNotTrue when got upgraded to PHPUnit 4.x - $this->assertEquals(false, $oProperties->isCustomPropertySet('key6')); + $this->assertNotTrue($oProperties->isCustomPropertySet('key6')); $this->assertEquals(array('key1', 'key2', 'key3', 'key4', 'key5'), $oProperties->getCustomProperties()); } @@ -211,8 +210,7 @@ class DocInfoTest extends \PHPUnit_Framework_TestCase $this->assertEquals('8.3', DocInfo::convertProperty('8.3', 'lpstr')); $this->assertEquals(strtotime('10/11/2013'), DocInfo::convertProperty('10/11/2013', 'date')); $this->assertTrue(DocInfo::convertProperty('true', 'bool')); - // todo: change to assertNotTrue when got upgraded to PHPUnit 4.x - $this->assertEquals(false, DocInfo::convertProperty('1', 'bool')); + $this->assertNotTrue(DocInfo::convertProperty('1', 'bool')); $this->assertEquals('1', DocInfo::convertProperty('1', 'array')); $this->assertEquals('1', DocInfo::convertProperty('1', '')); diff --git a/tests/PhpWord/Metadata/SettingsTest.php b/tests/PhpWord/Metadata/SettingsTest.php new file mode 100644 index 00000000..9670f1d9 --- /dev/null +++ b/tests/PhpWord/Metadata/SettingsTest.php @@ -0,0 +1,229 @@ +setEvenAndOddHeaders(true); + $this->assertTrue($oSettings->hasEvenAndOddHeaders()); + } + + /** + * HideGrammaticalErrors + */ + public function testHideGrammaticalErrors() + { + $oSettings = new Settings(); + $oSettings->setHideGrammaticalErrors(true); + $this->assertTrue($oSettings->hasHideGrammaticalErrors()); + } + + /** + * HideSpellingErrors + */ + public function testHideSpellingErrors() + { + $oSettings = new Settings(); + $oSettings->setHideSpellingErrors(true); + $this->assertTrue($oSettings->hasHideSpellingErrors()); + } + + /** + * DocumentProtection + */ + public function testDocumentProtection() + { + $oSettings = new Settings(); + $oSettings->setDocumentProtection(new Protection('trackedChanges')); + $this->assertNotNull($oSettings->getDocumentProtection()); + + $this->assertEquals('trackedChanges', $oSettings->getDocumentProtection()->getEditing()); + } + + /** + * Test setting an invalid salt + * @expectedException \InvalidArgumentException + */ + public function testInvalidSalt() + { + $protection = new Protection(); + $protection->setSalt('123'); + } + + /** + * TrackRevistions + */ + public function testTrackRevisions() + { + $oSettings = new Settings(); + $oSettings->setTrackRevisions(true); + $this->assertTrue($oSettings->hasTrackRevisions()); + } + + /** + * DoNotTrackFormatting + */ + public function testDoNotTrackFormatting() + { + $oSettings = new Settings(); + $oSettings->setDoNotTrackFormatting(true); + $this->assertTrue($oSettings->hasDoNotTrackFormatting()); + } + + /** + * DoNotTrackMoves + */ + public function testDoNotTrackMoves() + { + $oSettings = new Settings(); + $oSettings->setDoNotTrackMoves(true); + $this->assertTrue($oSettings->hasDoNotTrackMoves()); + } + + /** + * ProofState + */ + public function testProofState() + { + $proofState = new ProofState(); + $proofState->setGrammar(ProofState::CLEAN); + $proofState->setSpelling(ProofState::DIRTY); + + $oSettings = new Settings(); + $oSettings->setProofState($proofState); + $this->assertNotNull($oSettings->getProofState()); + $this->assertEquals(ProofState::CLEAN, $oSettings->getProofState()->getGrammar()); + $this->assertEquals(ProofState::DIRTY, $oSettings->getProofState()->getSpelling()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWrongProofStateGrammar() + { + $proofState = new ProofState(); + $proofState->setGrammar('wrong'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWrongProofStateSpelling() + { + $proofState = new ProofState(); + $proofState->setSpelling('wrong'); + } + + /** + * Zoom as percentage + */ + public function testZoomPercentage() + { + $oSettings = new Settings(); + $oSettings->setZoom(75); + $this->assertEquals(75, $oSettings->getZoom()); + } + + /** + * Zoom as string + */ + public function testZoomEnum() + { + $oSettings = new Settings(); + $oSettings->setZoom(Zoom::FULL_PAGE); + $this->assertEquals('fullPage', $oSettings->getZoom()); + } + + /** + * Test Update Fields on update + */ + public function testUpdateFields() + { + $oSettings = new Settings(); + $oSettings->setUpdateFields(true); + $this->assertTrue($oSettings->hasUpdateFields()); + } + + public function testAutoHyphenation() + { + $oSettings = new Settings(); + $oSettings->setAutoHyphenation(true); + $this->assertTrue($oSettings->hasAutoHyphenation()); + } + + public function testDefaultAutoHyphenation() + { + $oSettings = new Settings(); + $this->assertNull($oSettings->hasAutoHyphenation()); + } + + public function testConsecutiveHyphenLimit() + { + $consecutiveHypenLimit = 2; + $oSettings = new Settings(); + $oSettings->setConsecutiveHyphenLimit($consecutiveHypenLimit); + $this->assertSame($consecutiveHypenLimit, $oSettings->getConsecutiveHyphenLimit()); + } + + public function testDefaultConsecutiveHyphenLimit() + { + $oSettings = new Settings(); + $this->assertNull($oSettings->getConsecutiveHyphenLimit()); + } + + public function testHyphenationZone() + { + $hyphenationZoneInTwip = 100; + $oSettings = new Settings(); + $oSettings->setHyphenationZone($hyphenationZoneInTwip); + $this->assertSame($hyphenationZoneInTwip, $oSettings->getHyphenationZone()); + } + + public function testDefaultHyphenationZone() + { + $oSettings = new Settings(); + $this->assertNull($oSettings->getHyphenationZone()); + } + + public function testDoNotHyphenateCaps() + { + $oSettings = new Settings(); + $oSettings->setDoNotHyphenateCaps(true); + $this->assertTrue($oSettings->hasDoNotHyphenateCaps()); + } + + public function testDefaultDoNotHyphenateCaps() + { + $oSettings = new Settings(); + $this->assertNull($oSettings->hasDoNotHyphenateCaps()); + } +} diff --git a/tests/PhpWord/PhpWordTest.php b/tests/PhpWord/PhpWordTest.php index 459c67a0..d818e0f8 100644 --- a/tests/PhpWord/PhpWordTest.php +++ b/tests/PhpWord/PhpWordTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Metadata\DocInfo; * * @runTestsInSeparateProcesses */ -class PhpWordTest extends \PHPUnit_Framework_TestCase +class PhpWordTest extends \PHPUnit\Framework\TestCase { /** * Test object creation @@ -99,7 +99,6 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase $phpWord->$method($styleId, array()); $this->assertInstanceOf("PhpOffice\\PhpWord\\Style\\{$value}", Style::getStyle($styleId)); } - } /** @@ -139,7 +138,7 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase */ public function testLoadTemplateException() { - $templateFqfn = join( + $templateFqfn = implode( DIRECTORY_SEPARATOR, array(PHPWORD_TESTS_BASE_DIR, 'PhpWord', 'Tests', '_files', 'templates', 'blanks.docx') ); @@ -152,6 +151,8 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase */ public function testSave() { + $this->setOutputCallback(function () { + }); $phpWord = new PhpWord(); $section = $phpWord->addSection(); $section->addText('Hello world!'); @@ -170,4 +171,58 @@ class PhpWordTest extends \PHPUnit_Framework_TestCase $phpWord = new PhpWord(); $phpWord->undefinedMethod(); } + + /** + * @covers \PhpOffice\PhpWord\PhpWord::getSection + */ + public function testGetNotExistingSection() + { + $phpWord = new PhpWord(); + $section = $phpWord->getSection(0); + + $this->assertNull($section); + } + + /** + * @covers \PhpOffice\PhpWord\PhpWord::getSection + */ + public function testGetSection() + { + $phpWord = new PhpWord(); + $phpWord->addSection(); + $section = $phpWord->getSection(0); + + $this->assertNotNull($section); + } + + /** + * @covers \PhpOffice\PhpWord\PhpWord::sortSections + */ + public function testSortSections() + { + $phpWord = new PhpWord(); + $section1 = $phpWord->addSection(); + $section1->addText('test1'); + $section2 = $phpWord->addSection(); + $section2->addText('test2'); + $section2->addText('test3'); + + $this->assertEquals(1, $phpWord->getSection(0)->countElements()); + $this->assertEquals(2, $phpWord->getSection(1)->countElements()); + + $phpWord->sortSections(function ($a, $b) { + $numElementsInA = $a->countElements(); + $numElementsInB = $b->countElements(); + if ($numElementsInA === $numElementsInB) { + return 0; + } elseif ($numElementsInA > $numElementsInB) { + return -1; + } + + return 1; + }); + + $this->assertEquals(2, $phpWord->getSection(0)->countElements()); + $this->assertEquals(1, $phpWord->getSection(1)->countElements()); + } } diff --git a/tests/PhpWord/Reader/HTMLTest.php b/tests/PhpWord/Reader/HTMLTest.php index 6e3039cf..38588afc 100644 --- a/tests/PhpWord/Reader/HTMLTest.php +++ b/tests/PhpWord/Reader/HTMLTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\IOFactory; * @coversDefaultClass \PhpOffice\PhpWord\Reader\HTML * @runTestsInSeparateProcesses */ -class HTMLTest extends \PHPUnit_Framework_TestCase +class HTMLTest extends \PHPUnit\Framework\TestCase { /** * Test load diff --git a/tests/PhpWord/Reader/MsDocTest.php b/tests/PhpWord/Reader/MsDocTest.php new file mode 100644 index 00000000..3ce39939 --- /dev/null +++ b/tests/PhpWord/Reader/MsDocTest.php @@ -0,0 +1,79 @@ +assertTrue($object->canRead($filename)); + } + + /** + * Can read exception + */ + public function testCanReadFailed() + { + $object = new MsDoc(); + $filename = __DIR__ . '/../_files/documents/foo.doc'; + $this->assertFalse($object->canRead($filename)); + } + + /** + * Load + */ + public function testLoad() + { + $filename = __DIR__ . '/../_files/documents/reader.doc'; + $phpWord = IOFactory::load($filename, 'MsDoc'); + $this->assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $phpWord); + } + + /** + * Test exception on not existing file + * @expectedException \Exception + */ + public function testFailIfFileNotReadable() + { + $filename = __DIR__ . '/../_files/documents/not_existing_reader.doc'; + IOFactory::load($filename, 'MsDoc'); + } + + /** + * Test exception on non OLE document + * @expectedException \Exception + */ + public function testFailIfFileNotOle() + { + $filename = __DIR__ . '/../_files/documents/reader.odt'; + IOFactory::load($filename, 'MsDoc'); + } +} diff --git a/tests/PhpWord/Reader/ODTextTest.php b/tests/PhpWord/Reader/ODTextTest.php index 1bdce2e6..ad270864 100644 --- a/tests/PhpWord/Reader/ODTextTest.php +++ b/tests/PhpWord/Reader/ODTextTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\IOFactory; * @coversDefaultClass \PhpOffice\PhpWord\Reader\ODText * @runTestsInSeparateProcesses */ -class ODTextTest extends \PHPUnit_Framework_TestCase +class ODTextTest extends \PHPUnit\Framework\TestCase { /** * Load diff --git a/tests/PhpWord/Reader/RTFTest.php b/tests/PhpWord/Reader/RTFTest.php index 79cf13a7..fed00ceb 100644 --- a/tests/PhpWord/Reader/RTFTest.php +++ b/tests/PhpWord/Reader/RTFTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\IOFactory; * @coversDefaultClass \PhpOffice\PhpWord\Reader\RTF * @runTestsInSeparateProcesses */ -class RTFTest extends \PHPUnit_Framework_TestCase +class RTFTest extends \PHPUnit\Framework\TestCase { /** * Test load diff --git a/tests/PhpWord/Reader/Word2007/ElementTest.php b/tests/PhpWord/Reader/Word2007/ElementTest.php new file mode 100644 index 00000000..cb72ef9f --- /dev/null +++ b/tests/PhpWord/Reader/Word2007/ElementTest.php @@ -0,0 +1,275 @@ + + + + test string + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\TextRun $textRun */ + $textRun = $elements[0]; + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextBreak', $textRun->getElement(0)); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(1)); + $this->assertEquals('test string', $textRun->getElement(1)->getText()); + } + + /** + * Test reading content inside w:smartTag + */ + public function testSmartTag() + { + $documentXml = ' + + + test string + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\TextRun $textRun */ + $textRun = $elements[0]; + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0)); + $this->assertEquals('test string', $textRun->getElement(0)->getText()); + } + + /** + * Test reading of textbreak + */ + public function testReadListItemRunWithFormatting() + { + $documentXml = ' + + + + + + + + Two + + + with + + + + + + bold + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $sections = $phpWord->getSection(0); + $this->assertNull($sections->getElement(999)); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\ListItemRun', $sections->getElement(0)); + $this->assertEquals(0, $sections->getElement(0)->getDepth()); + + $listElements = $sections->getElement(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $listElements[0]); + $this->assertEquals('Two', $listElements[0]->getText()); + $this->assertEquals(' with ', $listElements[1]->getText()); + $this->assertEquals('bold', $listElements[2]->getText()); + $this->assertTrue($listElements[2]->getFontStyle()->getBold()); + } + + /** + * Test reading track changes + */ + public function testReadTrackChange() + { + $documentXml = ' + + One + + + + two + + + + + three + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\TextRun $elements */ + $textRun = $elements[0]; + + $this->assertEquals('One ', $textRun->getElement(0)->getText()); + + $this->assertEquals('two', $textRun->getElement(1)->getText()); + $this->assertNotNull($textRun->getElement(1)->getTrackChange()); + /** @var \PhpOffice\PhpWord\Element\TrackChange $trackChange */ + $trackChange = $textRun->getElement(1)->getTrackChange(); + $this->assertEquals(TrackChange::DELETED, $trackChange->getChangeType()); + + $this->assertEquals('three', $textRun->getElement(2)->getText()); + $this->assertNotNull($textRun->getElement(2)->getTrackChange()); + /** @var \PhpOffice\PhpWord\Element\TrackChange $trackChange */ + $trackChange = $textRun->getElement(2)->getTrackChange(); + $this->assertEquals(TrackChange::INSERTED, $trackChange->getChangeType()); + } + + /** + * Test reading of tab + */ + public function testReadTab() + { + $documentXml = ' + + One + + Two + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\TextRun $textRun */ + $textRun = $elements[0]; + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0)); + $this->assertEquals('One', $textRun->getElement(0)->getText()); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(1)); + $this->assertEquals("\t", $textRun->getElement(1)->getText()); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(2)); + $this->assertEquals('Two', $textRun->getElement(2)->getText()); + } + + /** + * Test reading Title style + */ + public function testReadTitleStyle() + { + $documentXml = ' + + + + + This is a non formatted title + + + + + + + + This is a + + + + + + bold + + + title + + '; + + $stylesXml = ' + + + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml, 'styles' => $stylesXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Title', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\Title $title */ + $title = $elements[0]; + $this->assertEquals('Title', $title->getStyle()); + $this->assertEquals('This is a non formatted title', $title->getText()); + + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Title', $elements[1]); + /** @var \PhpOffice\PhpWord\Element\Title $formattedTitle */ + $formattedTitle = $elements[1]; + $this->assertEquals('Title', $formattedTitle->getStyle()); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $formattedTitle->getText()); + } + + /** + * Test reading Drawing + */ + public function testReadDrawing() + { + $documentXml = ' + + + + + + + + + + + + + + + + + + + + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + } +} diff --git a/tests/PhpWord/Reader/Word2007/PartTest.php b/tests/PhpWord/Reader/Word2007/PartTest.php new file mode 100644 index 00000000..31a492b8 --- /dev/null +++ b/tests/PhpWord/Reader/Word2007/PartTest.php @@ -0,0 +1,163 @@ + + + This is a test + + + + + + + + + + + And another one + + + + + + + + '; + + $footnotesXml = ' + + + + + + + + + + + + + + + + + + + + + + footnote text + + + '; + + $endnotesXml = ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is an endnote + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml, 'footnotes' => $footnotesXml, 'endnotes' => $endnotesXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + /** @var \PhpOffice\PhpWord\Element\TextRun $textRun */ + $textRun = $elements[0]; + + //test the text in the first paragraph + /** @var \PhpOffice\PhpWord\Element\Text $text */ + $text = $elements[0]->getElement(0); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $text); + $this->assertEquals('This is a test', $text->getText()); + + //test the presence of the footnote in the document.xml + /** @var \PhpOffice\PhpWord\Element\Footnote $footnote */ + $documentFootnote = $textRun->getElement(1); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Footnote', $documentFootnote); + $this->assertEquals(1, $documentFootnote->getRelationId()); + + //test the presence of the footnote in the footnote.xml + /** @var \PhpOffice\PhpWord\Element\Footnote $footnote */ + $footnote = $phpWord->getFootnotes()->getItem(1); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Footnote', $footnote); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $footnote->getElement(0)); + $this->assertEquals('footnote text', $footnote->getElement(0)->getText()); + $this->assertEquals(1, $footnote->getRelationId()); + + //test the text in the second paragraph + /** @var \PhpOffice\PhpWord\Element\Text $text */ + $text = $elements[1]->getElement(0); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $text); + $this->assertEquals('And another one', $text->getText()); + + //test the presence of the endnote in the document.xml + /** @var \PhpOffice\PhpWord\Element\Endnote $endnote */ + $documentEndnote = $elements[1]->getElement(1); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Endnote', $documentEndnote); + $this->assertEquals(2, $documentEndnote->getRelationId()); + + //test the presence of the endnote in the endnote.xml + /** @var \PhpOffice\PhpWord\Element\Endnote $endnote */ + $endnote = $phpWord->getEndnotes()->getItem(1); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Endnote', $endnote); + $this->assertEquals(2, $endnote->getRelationId()); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $endnote->getElement(0)); + $this->assertEquals('This is an endnote', $endnote->getElement(0)->getText()); + } +} diff --git a/tests/PhpWord/Reader/Word2007/StyleTest.php b/tests/PhpWord/Reader/Word2007/StyleTest.php new file mode 100644 index 00000000..d64079fa --- /dev/null +++ b/tests/PhpWord/Reader/Word2007/StyleTest.php @@ -0,0 +1,148 @@ + + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); + $this->assertEquals(Table::LAYOUT_FIXED, $elements[0]->getStyle()->getLayout()); + } + + /** + * Test reading of cell spacing + */ + public function testReadCellSpacing() + { + $documentXml = ' + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); + /** @var \PhpOffice\PhpWord\Style\Table $tableStyle */ + $tableStyle = $elements[0]->getStyle(); + $this->assertEquals(TblWidth::AUTO, $tableStyle->getUnit()); + $this->assertEquals(10.5, $tableStyle->getCellSpacing()); + } + + /** + * Test reading of table position + */ + public function testReadTablePosition() + { + $documentXml = ' + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); + $this->assertNotNull($elements[0]->getStyle()->getPosition()); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\TablePosition', $elements[0]->getStyle()->getPosition()); + /** @var \PhpOffice\PhpWord\Style\TablePosition $tableStyle */ + $tableStyle = $elements[0]->getStyle()->getPosition(); + $this->assertEquals(10, $tableStyle->getLeftFromText()); + $this->assertEquals(20, $tableStyle->getRightFromText()); + $this->assertEquals(30, $tableStyle->getTopFromText()); + $this->assertEquals(40, $tableStyle->getBottomFromText()); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $tableStyle->getVertAnchor()); + $this->assertEquals(TablePosition::HANCHOR_MARGIN, $tableStyle->getHorzAnchor()); + $this->assertEquals(TablePosition::XALIGN_CENTER, $tableStyle->getTblpXSpec()); + $this->assertEquals(50, $tableStyle->getTblpX()); + $this->assertEquals(TablePosition::YALIGN_TOP, $tableStyle->getTblpYSpec()); + $this->assertEquals(60, $tableStyle->getTblpY()); + } + + /** + * Test reading of position + */ + public function testReadPosition() + { + $documentXml = ' + + + + + This text is lowered + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + /** @var \PhpOffice\PhpWord\Element\TextRun $elements */ + $textRun = $elements[0]; + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $textRun); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0)); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Font', $textRun->getElement(0)->getFontStyle()); + /** @var \PhpOffice\PhpWord\Style\Font $fontStyle */ + $fontStyle = $textRun->getElement(0)->getFontStyle(); + $this->assertEquals(15, $fontStyle->getPosition()); + } + + public function testReadIndent() + { + $documentXml = ' + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); + /** @var \PhpOffice\PhpWord\Style\Table $tableStyle */ + $tableStyle = $elements[0]->getStyle(); + $this->assertSame(TblWidth::TWIP, $tableStyle->getIndent()->getType()); + $this->assertSame(2160, $tableStyle->getIndent()->getValue()); + } +} diff --git a/tests/PhpWord/Reader/Word2007Test.php b/tests/PhpWord/Reader/Word2007Test.php index 9be78a5b..e4ea62de 100644 --- a/tests/PhpWord/Reader/Word2007Test.php +++ b/tests/PhpWord/Reader/Word2007Test.php @@ -10,14 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Reader; use PhpOffice\PhpWord\IOFactory; +use PhpOffice\PhpWord\TestHelperDOCX; /** * Test class for PhpOffice\PhpWord\Reader\Word2007 @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\IOFactory; * @coversDefaultClass \PhpOffice\PhpWord\Reader\Word2007 * @runTestsInSeparateProcesses */ -class Word2007Test extends \PHPUnit_Framework_TestCase +class Word2007Test extends \PHPUnit\Framework\TestCase { /** * Test canRead() method @@ -54,6 +55,27 @@ class Word2007Test extends \PHPUnit_Framework_TestCase { $filename = __DIR__ . '/../_files/documents/reader.docx'; $phpWord = IOFactory::load($filename); + $this->assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $phpWord); + $this->assertTrue($phpWord->getSettings()->hasDoNotTrackMoves()); + $this->assertFalse($phpWord->getSettings()->hasDoNotTrackFormatting()); + $this->assertEquals(100, $phpWord->getSettings()->getZoom()); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertFalse($doc->elementExists('/w:document/w:body/w:p/w:r[w:t/node()="italics"]/w:rPr/w:b')); + } + + /** + * Load a Word 2011 file + */ + public function testLoadWord2011() + { + $filename = __DIR__ . '/../_files/documents/reader-2011.docx'; + $phpWord = IOFactory::load($filename); + + $this->assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $phpWord); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[3]/w:r/w:pict/v:shape/v:imagedata')); } } diff --git a/tests/PhpWord/SettingsTest.php b/tests/PhpWord/SettingsTest.php index f5ac3ed6..afe59549 100644 --- a/tests/PhpWord/SettingsTest.php +++ b/tests/PhpWord/SettingsTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord; * @coversDefaultClass \PhpOffice\PhpWord\Settings * @runTestsInSeparateProcesses */ -class SettingsTest extends \PHPUnit_Framework_TestCase +class SettingsTest extends \PHPUnit\Framework\TestCase { /** * Test set/get compatibity option @@ -78,7 +78,6 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $this->assertEquals(sys_get_temp_dir(), Settings::getTempDir()); } - /** * @covers ::setTempDir * @covers ::getTempDir diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index e307f09b..fbe92c25 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Shared; * * @coversDefaultClass \PhpOffice\PhpWord\Shared\Converter */ -class ConverterTest extends \PHPUnit_Framework_TestCase +class ConverterTest extends \PHPUnit\Framework\TestCase { /** * Test unit conversion functions with various numbers @@ -73,7 +73,7 @@ class ConverterTest extends \PHPUnit_Framework_TestCase $result = Converter::pixelToPoint($value); $this->assertEquals($value / 96 * 72, $result); - $result = Converter::pixelToEMU($value); + $result = Converter::pixelToEmu($value); $this->assertEquals(round($value * 9525), $result); $result = Converter::pointToTwip($value); @@ -82,14 +82,17 @@ class ConverterTest extends \PHPUnit_Framework_TestCase $result = Converter::pointToPixel($value); $this->assertEquals($value / 72 * 96, $result); - $result = Converter::pointToEMU($value); + $result = Converter::pointToEmu($value); $this->assertEquals(round($value / 72 * 96 * 9525), $result); $result = Converter::emuToPixel($value); $this->assertEquals(round($value / 9525), $result); + $result = Converter::picaToPoint($value); + $this->assertEquals($value / 6 * 72, $result, '', 0.00001); + $result = Converter::degreeToAngle($value); - $this->assertEquals((int)round($value * 60000), $result); + $this->assertEquals((int) round($value * 60000), $result); $result = Converter::angleToDegree($value); $this->assertEquals(round($value / 60000), $result); @@ -108,8 +111,24 @@ class ConverterTest extends \PHPUnit_Framework_TestCase $values[] = array('0F9D', false); // 4 characters // Conduct test foreach ($values as $value) { - $result = Converter::htmlToRGB($value[0]); + $result = Converter::htmlToRgb($value[0]); $this->assertEquals($value[1], $result); } } + + /** + * Test css size to point + */ + public function testCssSizeParser() + { + $this->assertNull(Converter::cssToPoint('10em')); + $this->assertEquals(0, Converter::cssToPoint('0')); + $this->assertEquals(10, Converter::cssToPoint('10pt')); + $this->assertEquals(7.5, Converter::cssToPoint('10px')); + $this->assertEquals(720, Converter::cssToPoint('10in')); + $this->assertEquals(7.2, Converter::cssToPoint('0.1in')); + $this->assertEquals(120, Converter::cssToPoint('10pc')); + $this->assertEquals(28.346457, Converter::cssToPoint('10mm'), '', 0.000001); + $this->assertEquals(283.464567, Converter::cssToPoint('10cm'), '', 0.000001); + } } diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index c651fd4a..89292a20 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -10,19 +10,24 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Shared; use PhpOffice\PhpWord\Element\Section; +use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\LineSpacingRule; +use PhpOffice\PhpWord\Style\Paragraph; +use PhpOffice\PhpWord\TestHelperDOCX; /** * Test class for PhpOffice\PhpWord\Shared\Html + * @coversDefaultClass \PhpOffice\PhpWord\Shared\Html */ -class HtmlTest extends \PHPUnit_Framework_TestCase +class HtmlTest extends \PHPUnit\Framework\TestCase { /** * Test unit conversion functions with various numbers @@ -32,7 +37,8 @@ class HtmlTest extends \PHPUnit_Framework_TestCase $content = ''; // Default - $section = new Section(1); + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); $this->assertCount(0, $section->getElements()); // Heading @@ -43,7 +49,7 @@ class HtmlTest extends \PHPUnit_Framework_TestCase // Styles $content .= '

    '; + . 'text-align: center; color: #999; background-color: #000; font-weight: bold; font-style: italic;">'; foreach ($styles as $style) { $content .= "<{$style}>{$style}"; } @@ -54,7 +60,7 @@ class HtmlTest extends \PHPUnit_Framework_TestCase $this->assertCount(7, $section->getElements()); // Other parts - $section = new Section(1); + $section = $phpWord->addSection(); $content = ''; $content .= '

    HeaderContent
    '; $content .= '
    • Bullet
      • Bullet
    '; @@ -67,4 +73,520 @@ class HtmlTest extends \PHPUnit_Framework_TestCase $content .= '–   ²³¼½¾'; Html::addHtml($section, $content); } + + /** + * Test that html already in body element can be read + * @ignore + */ + public function testParseFullHtml() + { + $section = new Section(1); + Html::addHtml($section, '

    test paragraph1

    test paragraph2

    ', true); + + $this->assertCount(2, $section->getElements()); + } + + /** + * Test HTML entities + */ + public function testParseHtmlEntities() + { + \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true); + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, 'text with entities <my text>'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:r/w:t')); + $this->assertEquals('text with entities ', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue); + } + + /** + * Test underline + */ + public function testParseUnderline() + { + $html = 'test'; + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:u')); + $this->assertEquals('single', $doc->getElementAttribute('/w:document/w:body/w:p/w:r/w:rPr/w:u', 'w:val')); + } + + /** + * Test text-decoration style + */ + public function testParseTextDecoration() + { + $html = 'test'; + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:u')); + $this->assertEquals('single', $doc->getElementAttribute('/w:document/w:body/w:p/w:r/w:rPr/w:u', 'w:val')); + } + + /** + * Test font + */ + public function testParseFont() + { + $html = 'test'; + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr')); + //TODO check style + } + + /** + * Test line-height style + */ + public function testParseLineHeight() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:pPr/w:spacing')); + $this->assertEquals(Paragraph::LINE_HEIGHT * 1.5, $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:spacing', 'w:line')); + $this->assertEquals(LineSpacingRule::AUTO, $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:spacing', 'w:lineRule')); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:pPr/w:spacing')); + $this->assertEquals(300, $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:pPr/w:spacing', 'w:line')); + $this->assertEquals(LineSpacingRule::EXACT, $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:pPr/w:spacing', 'w:lineRule')); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[3]/w:pPr/w:spacing')); + $this->assertEquals(Paragraph::LINE_HEIGHT * 1.2, $doc->getElementAttribute('/w:document/w:body/w:p[3]/w:pPr/w:spacing', 'w:line')); + $this->assertEquals(LineSpacingRule::AUTO, $doc->getElementAttribute('/w:document/w:body/w:p[3]/w:pPr/w:spacing', 'w:lineRule')); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[4]/w:pPr/w:spacing')); + $this->assertEquals(244.8, $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:spacing', 'w:line')); + $this->assertEquals(LineSpacingRule::EXACT, $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:spacing', 'w:lineRule')); + } + + /** + * Test text-indent style + */ + public function testParseTextIndent() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, '

    test

    '); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:ind')); + $this->assertEquals(750, $doc->getElementAttribute('/w:document/w:body/w:p/w:pPr/w:ind', 'w:firstLine')); + } + + /** + * Test text-align style + */ + public function testParseTextAlign() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + Html::addHtml($section, '

    test

    '); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:jc')); + $this->assertEquals(Jc::START, $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:jc', 'w:val')); + $this->assertEquals(Jc::END, $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:pPr/w:jc', 'w:val')); + $this->assertEquals(Jc::CENTER, $doc->getElementAttribute('/w:document/w:body/w:p[3]/w:pPr/w:jc', 'w:val')); + $this->assertEquals(Jc::BOTH, $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:jc', 'w:val')); + } + + /** + * Test font-size style + */ + public function testParseFontSize() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, 'test'); + Html::addHtml($section, 'test'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:sz')); + $this->assertEquals('20', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:r/w:rPr/w:sz', 'w:val')); + $this->assertEquals('15', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz', 'w:val')); + } + + /** + * Test direction style + */ + public function testParseTextDirection() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, 'test'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:rtl')); + } + + /** + * Test html lang + */ + public function testParseLang() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, 'test'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:lang')); + $this->assertEquals('fr-BE', $doc->getElementAttribute('/w:document/w:body/w:p/w:r/w:rPr/w:lang', 'w:val')); + } + + /** + * Test font-family style + */ + public function testParseFontFamily() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, 'test'); + Html::addHtml($section, 'test'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:rFonts')); + $this->assertEquals('Arial', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:r/w:rPr/w:rFonts', 'w:ascii')); + $this->assertEquals('Times New Roman', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:rFonts', 'w:ascii')); + } + + /** + * Test parsing paragraph and span styles + */ + public function testParseParagraphAndSpanStyle() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + Html::addHtml($section, '

    test

    '); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:jc')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:spacing')); + $this->assertEquals(Jc::CENTER, $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:jc', 'w:val')); + $this->assertEquals('single', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:r/w:rPr/w:u', 'w:val')); + } + + /** + * Test parsing table + */ + public function testParseTable() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = ' + + + + + + + + + + + +
    header aheader bheader c
    12
    This is bold text5

    6

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tr/w:tc')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tblPr/w:jc')); + $this->assertEquals(Jc::START, $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tblPr/w:jc', 'w:val')); + } + + /** + * Tests parsing of ul/li + */ + public function testParseList() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '
      +
    • + + list item1 + +
    • +
    • + + list item2 + +
    • +
    '; + Html::addHtml($section, $html, false, false); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t')); + $this->assertEquals('list item1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue); + $this->assertEquals('list item2', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:b')); + } + + /** + * Tests parsing of ul/li + */ + public function testOrderedListNumbering() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '
      +
    1. List 1 item 1
    2. +
    3. List 1 item 2
    4. +
    +

    Some Text

    +
      +
    1. List 2 item 1
    2. +
    3. List 2 item 2
    4. +
    '; + Html::addHtml($section, $html, false, false); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t')); + + $this->assertEquals('List 1 item 1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue); + $this->assertEquals('List 2 item 1', $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:t')->nodeValue); + + $firstListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId', 'w:val'); + $secondListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:numPr/w:numId', 'w:val'); + + $this->assertNotEquals($firstListnumId, $secondListnumId); + } + + /** + * Tests parsing of nested ul/li + */ + public function testOrderedNestedListNumbering() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '
      +
    1. List 1 item 1
    2. +
    3. List 1 item 2
    4. +
    +

    Some Text

    +
      +
    1. List 2 item 1
    2. +
    3. +
        +
      1. sub list 1
      2. +
      3. sub list 2
      4. +
      +
    4. +
    '; + Html::addHtml($section, $html, false, false); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t')); + + $this->assertEquals('List 1 item 1', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue); + $this->assertEquals('List 2 item 1', $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:t')->nodeValue); + + $firstListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId', 'w:val'); + $secondListnumId = $doc->getElementAttribute('/w:document/w:body/w:p[4]/w:pPr/w:numPr/w:numId', 'w:val'); + + $this->assertNotEquals($firstListnumId, $secondListnumId); + } + + /** + * Tests parsing of ul/li + */ + public function testParseListWithFormat() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = preg_replace('/\s+/', ' ', '
      +
    • Some text before + + list item1 bold with text after bold + + and some after +
    • +
    • + + list item2 + +
    • +
    '); + Html::addHtml($section, $html, false, false); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:numPr/w:numId')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t')); + $this->assertEquals('list item2', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:r[3]/w:rPr/w:b')); + $this->assertEquals('bold', $doc->getElement('/w:document/w:body/w:p[1]/w:r[3]/w:t')->nodeValue); + } + + /** + * Tests parsing of br + */ + public function testParseLineBreak() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    This is some text
    with a linebreak.

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:br')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t')); + $this->assertEquals('This is some text', $doc->getElement('/w:document/w:body/w:p/w:r[1]/w:t')->nodeValue); + $this->assertEquals('with a linebreak.', $doc->getElement('/w:document/w:body/w:p/w:r[2]/w:t')->nodeValue); + } + + /** + * Test parsing of img + */ + public function testParseImage() + { + $src = __DIR__ . '/../_files/images/firefox.png'; + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $baseXpath = '/w:document/w:body/w:p/w:r'; + $this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape')); + $this->assertStringMatchesFormat('%Swidth:150px%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style')); + $this->assertStringMatchesFormat('%Sheight:200px%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style')); + $this->assertStringMatchesFormat('%Smso-position-horizontal:right%S', $doc->getElementAttribute($baseXpath . '[1]/w:pict/v:shape', 'style')); + $this->assertStringMatchesFormat('%Smso-position-horizontal:left%S', $doc->getElementAttribute($baseXpath . '[2]/w:pict/v:shape', 'style')); + } + + /** + * Test parsing of remote img + */ + public function testParseRemoteImage() + { + $src = 'https://phpword.readthedocs.io/en/latest/_images/phpword.png'; + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $baseXpath = '/w:document/w:body/w:p/w:r'; + $this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape')); + } + + /** + * Test parsing embedded image + */ + public function testParseEmbeddedImage() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $baseXpath = '/w:document/w:body/w:p/w:r'; + $this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape')); + } + + /** + * Test parsing of remote img that can be found locally + */ + public function testParseRemoteLocalImage() + { + $src = 'https://fakedomain.io/images/firefox.png'; + $localPath = __DIR__ . '/../_files/images/'; + $options = array( + 'IMG_SRC_SEARCH' => 'https://fakedomain.io/images/', + 'IMG_SRC_REPLACE' => $localPath, + ); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    '; + Html::addHtml($section, $html, false, true, $options); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $baseXpath = '/w:document/w:body/w:p/w:r'; + $this->assertTrue($doc->elementExists($baseXpath . '/w:pict/v:shape')); + } + + /** + * Test parsing of remote img that can be found locally + * + * @expectedException \Exception + */ + public function testCouldNotLoadImage() + { + $src = 'https://fakedomain.io/images/firefox.png'; + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    '; + Html::addHtml($section, $html, false, true); + } + + public function testParseLink() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    link text

    '; + Html::addHtml($section, $html); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:hyperlink')); + $this->assertEquals('link text', $doc->getElement('/w:document/w:body/w:p/w:hyperlink/w:r/w:t')->nodeValue); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:hyperlink/w:r/w:rPr/w:u')); + $this->assertEquals('single', $doc->getElementAttribute('/w:document/w:body/w:p/w:hyperlink/w:r/w:rPr/w:u', 'w:val')); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addBookmark('bookmark'); + $html = '

    internal link text

    '; + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:hyperlink')); + $this->assertTrue($doc->getElement('/w:document/w:body/w:p/w:hyperlink')->hasAttribute('w:anchor')); + $this->assertEquals('bookmark', $doc->getElement('/w:document/w:body/w:p/w:hyperlink')->getAttribute('w:anchor')); + } + + public function testParseMalformedStyleIsIgnored() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $html = '

    text

    '; + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertFalse($doc->elementExists('/w:document/w:body/w:p[1]/w:pPr/w:jc')); + } } diff --git a/tests/PhpWord/Shared/ZipArchiveTest.php b/tests/PhpWord/Shared/ZipArchiveTest.php index 1adcfbfc..ecd0961e 100644 --- a/tests/PhpWord/Shared/ZipArchiveTest.php +++ b/tests/PhpWord/Shared/ZipArchiveTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,37 +25,36 @@ use PhpOffice\PhpWord\Settings; * @coversDefaultClass \PhpOffice\PhpWord\Shared\ZipArchive * @runTestsInSeparateProcesses */ -class ZipArchiveTest extends \PHPUnit_Framework_TestCase +class ZipArchiveTest extends \PHPUnit\Framework\TestCase { +// /** +// * Test close method exception: Working in local, not working in Travis +// * +// * expectedException \PhpOffice\PhpWord\Exception\Exception +// * expectedExceptionMessage Could not close zip file +// * covers ::close +// */ +// public function testCloseException() +// { +// $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; - /** - * Test close method exception: Working in local, not working in Travis - * - * expectedException \PhpOffice\PhpWord\Exception\Exception - * expectedExceptionMessage Could not close zip file - * covers ::close - */ - public function testCloseException() - { - // $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; +// $object = new ZipArchive(); +// $object->open($zipFile, ZipArchive::CREATE); +// $object->addFromString('content/string.txt', 'Test'); - // $object = new ZipArchive(); - // $object->open($zipFile, ZipArchive::CREATE); - // $object->addFromString('content/string.txt', 'Test'); +// // Lock the file +// $resource = fopen($zipFile, "w"); +// flock($resource, LOCK_EX); - // // Lock the file - // $resource = fopen($zipFile, "w"); - // flock($resource, LOCK_EX); +// // Closing the file should throws an exception +// $object->close(); - // // Closing the file should throws an exception - // $object->close(); +// // Unlock the file +// flock($resource, LOCK_UN); +// fclose($resource); - // // Unlock the file - // flock($resource, LOCK_UN); - // fclose($resource); - - // @unlink($zipFile); - } +// @unlink($zipFile); +// } /** * Test all methods diff --git a/tests/PhpWord/Style/AbstractStyleTest.php b/tests/PhpWord/Style/AbstractStyleTest.php index f7c6f6c5..7ec272bb 100644 --- a/tests/PhpWord/Style/AbstractStyleTest.php +++ b/tests/PhpWord/Style/AbstractStyleTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @runTestsInSeparateProcesses */ -class AbstractStyleTest extends \PHPUnit_Framework_TestCase +class AbstractStyleTest extends \PHPUnit\Framework\TestCase { /** * Test set style by array @@ -56,8 +56,7 @@ class AbstractStyleTest extends \PHPUnit_Framework_TestCase { $stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle'); - // todo: change to assertNotTrue when got upgraded to PHPUnit 4.x - $this->assertEquals(false, self::callProtectedMethod($stub, 'setBoolVal', array('a', false))); + $this->assertNotTrue(self::callProtectedMethod($stub, 'setBoolVal', array('a', false))); $this->assertEquals(200, self::callProtectedMethod($stub, 'setIntVal', array('foo', 200))); $this->assertEquals(2.1, self::callProtectedMethod($stub, 'setFloatVal', array('foo', 2.1))); $this->assertEquals('b', self::callProtectedMethod($stub, 'setEnumVal', array(null, array('a', 'b'), 'b'))); @@ -87,6 +86,7 @@ class AbstractStyleTest extends \PHPUnit_Framework_TestCase $class = new \ReflectionClass(get_class($object)); $method = $class->getMethod($method); $method->setAccessible(true); + return $method->invokeArgs($object, $args); } } diff --git a/tests/PhpWord/Style/CellTest.php b/tests/PhpWord/Style/CellTest.php index 51f4e895..db789fdc 100644 --- a/tests/PhpWord/Style/CellTest.php +++ b/tests/PhpWord/Style/CellTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Style\Cell * @runTestsInSeparateProcesses */ -class CellTest extends \PHPUnit_Framework_TestCase +class CellTest extends \PHPUnit\Framework\TestCase { /** * Test setting style with normal value diff --git a/tests/PhpWord/Style/ChartTest.php b/tests/PhpWord/Style/ChartTest.php new file mode 100644 index 00000000..9929a8f5 --- /dev/null +++ b/tests/PhpWord/Style/ChartTest.php @@ -0,0 +1,188 @@ +assertEquals($chart->getWidth(), 1000000); + + $chart->setWidth(200); + + $this->assertEquals($chart->getWidth(), 200); + } + + /** + * Testing getter and setter for chart height + */ + public function testSetGetHeight() + { + $chart = new Chart(); + + $this->assertEquals($chart->getHeight(), 1000000); + + $chart->setHeight(200); + + $this->assertEquals($chart->getHeight(), 200); + } + + /** + * Testing getter and setter for is3d + */ + public function testSetIs3d() + { + $chart = new Chart(); + + $this->assertEquals($chart->is3d(), false); + + $chart->set3d(true); + + $this->assertEquals($chart->is3d(), true); + } + + /** + * Testing getter and setter for chart colors + */ + public function testSetGetColors() + { + $chart = new Chart(); + + $this->assertInternalType('array', $chart->getColors()); + + $this->assertEquals(count($chart->getColors()), 0); + + $chart->setColors(array('FFFFFFFF', 'FF000000', 'FFFF0000')); + + $this->assertEquals($chart->getColors(), array('FFFFFFFF', 'FF000000', 'FFFF0000')); + } + + /** + * Testing getter and setter for dataLabelOptions + */ + public function testSetGetDataLabelOptions() + { + $chart = new Chart(); + + $originalDataLabelOptions = array( + 'showVal' => true, + 'showCatName' => true, + 'showLegendKey' => false, + 'showSerName' => false, + 'showPercent' => false, + 'showLeaderLines' => false, + 'showBubbleSize' => false, + ); + + $this->assertEquals($chart->getDataLabelOptions(), $originalDataLabelOptions); + + $changedDataLabelOptions = array( + 'showVal' => false, + 'showCatName' => false, + 'showLegendKey' => true, + 'showSerName' => true, + 'showPercent' => true, + 'showLeaderLines' => true, + 'showBubbleSize' => true, + ); + + $chart->setDataLabelOptions( + array( + 'showVal' => false, + 'showCatName' => false, + 'showLegendKey' => true, + 'showSerName' => true, + 'showPercent' => true, + 'showLeaderLines' => true, + 'showBubbleSize' => true, + ) + ); + $this->assertEquals($chart->getDataLabelOptions(), $changedDataLabelOptions); + } + + /** + * Testing categoryLabelPosition getter and setter + */ + public function testSetGetCategoryLabelPosition() + { + $chart = new Chart(); + + $this->assertEquals($chart->getCategoryLabelPosition(), 'nextTo'); + + $chart->setCategoryLabelPosition('high'); + + $this->assertEquals($chart->getCategoryLabelPosition(), 'high'); + } + + /** + * Testing valueLabelPosition getter and setter + */ + public function testSetGetValueLabelPosition() + { + $chart = new Chart(); + + $this->assertEquals($chart->getValueLabelPosition(), 'nextTo'); + + $chart->setValueLabelPosition('low'); + + $this->assertEquals($chart->getValueLabelPosition(), 'low'); + } + + /** + * Testing categoryAxisTitle getter and setter + */ + public function testSetGetCategoryAxisTitle() + { + $chart = new Chart(); + + $chart->getCategoryAxisTitle(); + + $this->assertEquals($chart->getCategoryAxisTitle(), null); + + $chart->setCategoryAxisTitle('Test Category Axis Title'); + + $this->assertEquals($chart->getCategoryAxisTitle(), 'Test Category Axis Title'); + } + + /** + * Testing valueAxisTitle getter and setter + */ + public function testSetGetValueAxisTitle() + { + $chart = new Chart(); + + $chart->getValueAxisTitle(); + + $this->assertEquals($chart->getValueAxisTitle(), null); + + $chart->setValueAxisTitle('Test Value Axis Title'); + + $this->assertEquals($chart->getValueAxisTitle(), 'Test Value Axis Title'); + } +} diff --git a/tests/PhpWord/Style/FontTest.php b/tests/PhpWord/Style/FontTest.php index 61648d4e..6a934579 100644 --- a/tests/PhpWord/Style/FontTest.php +++ b/tests/PhpWord/Style/FontTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -26,7 +26,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * * @runTestsInSeparateProcesses */ -class FontTest extends \PHPUnit_Framework_TestCase +class FontTest extends \PHPUnit\Framework\TestCase { /** * Tear down after each test @@ -45,7 +45,7 @@ class FontTest extends \PHPUnit_Framework_TestCase $this->assertEquals('text', $object->getStyleType()); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Paragraph', $object->getParagraphStyle()); - $this->assertTrue(is_array($object->getStyleValues())); + $this->assertInternalType('array', $object->getStyleValues()); } /** @@ -69,11 +69,13 @@ class FontTest extends \PHPUnit_Framework_TestCase 'doubleStrikethrough' => false, 'smallCaps' => false, 'allCaps' => false, + 'rtl' => false, 'fgColor' => null, 'bgColor' => null, 'scale' => null, 'spacing' => null, 'kerning' => null, + 'lang' => null, ); foreach ($attributes as $key => $default) { $get = is_bool($default) ? "is{$key}" : "get{$key}"; @@ -112,6 +114,9 @@ class FontTest extends \PHPUnit_Framework_TestCase 'scale' => 150, 'spacing' => 240, 'kerning' => 10, + 'rtl' => true, + 'noProof' => true, + 'lang' => new Language(Language::EN_US), ); $object->setStyleByArray($attributes); foreach ($attributes as $key => $value) { @@ -172,4 +177,15 @@ class FontTest extends \PHPUnit_Framework_TestCase $object = new Font(); $object->setLineHeight('a'); } + + /** + * Test setting the language as a string + */ + public function testSetLangAsString() + { + $object = new Font(); + $object->setLang(Language::FR_BE); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Language', $object->getLang()); + $this->assertEquals(Language::FR_BE, $object->getLang()->getLatin()); + } } diff --git a/tests/PhpWord/Style/ImageTest.php b/tests/PhpWord/Style/ImageTest.php index c5bb5c7d..1d43d921 100644 --- a/tests/PhpWord/Style/ImageTest.php +++ b/tests/PhpWord/Style/ImageTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * @coversDefaultClass \PhpOffice\PhpWord\Style\Image * @runTestsInSeparateProcesses */ -class ImageTest extends \PHPUnit_Framework_TestCase +class ImageTest extends \PHPUnit\Framework\TestCase { /** * Test setting style with normal value @@ -35,12 +35,16 @@ class ImageTest extends \PHPUnit_Framework_TestCase $object = new Image(); $properties = array( - 'width' => 200, - 'height' => 200, - 'alignment' => Jc::START, - 'marginTop' => 240, - 'marginLeft' => 240, - 'wrappingStyle' => 'inline', + 'width' => 200, + 'height' => 200, + 'alignment' => Jc::START, + 'marginTop' => 240, + 'marginLeft' => 240, + 'wrappingStyle' => 'inline', + 'wrapDistanceLeft' => 10, + 'wrapDistanceRight' => 20, + 'wrapDistanceTop' => 30, + 'wrapDistanceBottom' => 40, ); foreach ($properties as $key => $value) { $set = "set{$key}"; @@ -58,16 +62,21 @@ class ImageTest extends \PHPUnit_Framework_TestCase $object = new Image(); $properties = array( - 'width' => 200, - 'height' => 200, - 'alignment' => Jc::START, - 'marginTop' => 240, - 'marginLeft' => 240, - 'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE, - 'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_CENTER, - 'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_VERTICAL_TOP, - 'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_COLUMN, - 'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_IMARGIN, + 'width' => 200, + 'height' => 200, + 'alignment' => Jc::START, + 'marginTop' => 240, + 'marginLeft' => 240, + 'position' => 10, + 'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE, + 'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_CENTER, + 'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_VERTICAL_TOP, + 'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_COLUMN, + 'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE_TO_IMARGIN, + 'wrapDistanceLeft' => 10, + 'wrapDistanceRight' => 20, + 'wrapDistanceTop' => 30, + 'wrapDistanceBottom' => 40, ); foreach ($properties as $key => $value) { $get = "get{$key}"; diff --git a/tests/PhpWord/Style/IndentationTest.php b/tests/PhpWord/Style/IndentationTest.php index 477e1314..b39a4d77 100644 --- a/tests/PhpWord/Style/IndentationTest.php +++ b/tests/PhpWord/Style/IndentationTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\Indentation */ -class IndentationTest extends \PHPUnit_Framework_TestCase +class IndentationTest extends \PHPUnit\Framework\TestCase { /** * Test get/set diff --git a/tests/PhpWord/Style/LanguageTest.php b/tests/PhpWord/Style/LanguageTest.php new file mode 100644 index 00000000..3bf516f8 --- /dev/null +++ b/tests/PhpWord/Style/LanguageTest.php @@ -0,0 +1,77 @@ + array(null, 'fr-BE'), + 'eastAsia' => array(null, 'ja-JP'), + 'bidirectional' => array(null, 'ar-SA'), + 'langId' => array(null, 1036), + ); + foreach ($properties as $property => $value) { + list($default, $expected) = $value; + $get = "get{$property}"; + $set = "set{$property}"; + + $this->assertEquals($default, $object->$get()); // Default value + + $object->$set($expected); + + $this->assertEquals($expected, $object->$get()); // New value + } + } + + /** + * Test throws exception if wrong locale is given + * + * @expectedException \InvalidArgumentException + */ + public function testWrongLanguage() + { + $language = new Language(); + $language->setLatin('fra'); + } + + /** + * Tests that a language can be set with just a 2 char code + */ + public function testShortLanguage() + { + //when + $language = new Language(); + $language->setLatin('fr'); + + //then + Assert::assertEquals('fr-FR', $language->getLatin()); + } +} diff --git a/tests/PhpWord/Style/LineNumberingTest.php b/tests/PhpWord/Style/LineNumberingTest.php index e8ef1367..0d3f4e05 100644 --- a/tests/PhpWord/Style/LineNumberingTest.php +++ b/tests/PhpWord/Style/LineNumberingTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\LineNumbering */ -class LineNumberingTest extends \PHPUnit_Framework_TestCase +class LineNumberingTest extends \PHPUnit\Framework\TestCase { /** * Test get/set diff --git a/tests/PhpWord/Style/LineTest.php b/tests/PhpWord/Style/LineTest.php index 98e20b3d..fba09f70 100644 --- a/tests/PhpWord/Style/LineTest.php +++ b/tests/PhpWord/Style/LineTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Style\Image * @runTestsInSeparateProcesses */ -class LineTest extends \PHPUnit_Framework_TestCase +class LineTest extends \PHPUnit\Framework\TestCase { /** * Test setting style with normal value diff --git a/tests/PhpWord/Style/ListItemTest.php b/tests/PhpWord/Style/ListItemTest.php index 2e8692e9..71598e80 100644 --- a/tests/PhpWord/Style/ListItemTest.php +++ b/tests/PhpWord/Style/ListItemTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Style\ListItem * @runTestsInSeparateProcesses */ -class ListItemTest extends \PHPUnit_Framework_TestCase +class ListItemTest extends \PHPUnit\Framework\TestCase { /** * Test construct diff --git a/tests/PhpWord/Style/NumberingLevelTest.php b/tests/PhpWord/Style/NumberingLevelTest.php index c6cee11c..008a1fc5 100644 --- a/tests/PhpWord/Style/NumberingLevelTest.php +++ b/tests/PhpWord/Style/NumberingLevelTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -24,7 +24,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class NumberingLevelTest extends \PHPUnit_Framework_TestCase +class NumberingLevelTest extends \PHPUnit\Framework\TestCase { /** * Test setting style with normal value diff --git a/tests/PhpWord/Style/NumberingTest.php b/tests/PhpWord/Style/NumberingTest.php index ee9c032c..2090f9f6 100644 --- a/tests/PhpWord/Style/NumberingTest.php +++ b/tests/PhpWord/Style/NumberingTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,28 +22,28 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\Numbering */ -class NumberingTest extends \PHPUnit_Framework_TestCase +class NumberingTest extends \PHPUnit\Framework\TestCase { /** * Test get/set */ public function testGetSetProperties() { - $this->object = new Numbering(); - $this->properties = array( + $object = new Numbering(); + $properties = array( 'numId' => array(null, 1), 'type' => array(null, 'singleLevel'), ); - foreach ($this->properties as $property => $value) { + foreach ($properties as $property => $value) { list($default, $expected) = $value; $get = "get{$property}"; $set = "set{$property}"; - $this->assertEquals($default, $this->object->$get()); // Default value + $this->assertEquals($default, $object->$get()); // Default value - $this->object->$set($expected); + $object->$set($expected); - $this->assertEquals($expected, $this->object->$get()); // New value + $this->assertEquals($expected, $object->$get()); // New value } } @@ -52,8 +52,8 @@ class NumberingTest extends \PHPUnit_Framework_TestCase */ public function testGetLevels() { - $this->object = new Numbering(); + $object = new Numbering(); - $this->assertEmpty($this->object->getLevels()); + $this->assertEmpty($object->getLevels()); } } diff --git a/tests/PhpWord/Style/PaperTest.php b/tests/PhpWord/Style/PaperTest.php new file mode 100644 index 00000000..f8f00701 --- /dev/null +++ b/tests/PhpWord/Style/PaperTest.php @@ -0,0 +1,71 @@ +assertEquals('A4', $object->getSize()); + } + + /** + * Test paper size for B5 format + */ + public function testB5Size() + { + $object = new Paper('B5'); + + $this->assertEquals('B5', $object->getSize()); + $this->assertEquals(9977.9527559055, $object->getWidth(), '', 0.000000001); + $this->assertEquals(14173.228346457, $object->getHeight(), '', 0.000000001); + } + + /** + * Test paper size for Folio format + */ + public function testFolioSize() + { + $object = new Paper(); + $object->setSize('Folio'); + + $this->assertEquals('Folio', $object->getSize()); + $this->assertEquals(12240, $object->getWidth(), '', 0.1); + $this->assertEquals(18720, $object->getHeight(), '', 0.1); + } +} diff --git a/tests/PhpWord/Style/ParagraphTest.php b/tests/PhpWord/Style/ParagraphTest.php index c0096b0b..62460738 100644 --- a/tests/PhpWord/Style/ParagraphTest.php +++ b/tests/PhpWord/Style/ParagraphTest.php @@ -10,14 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\SimpleType\LineSpacingRule; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * * @runTestsInSeparateProcesses */ -class ParagraphTest extends \PHPUnit_Framework_TestCase +class ParagraphTest extends \PHPUnit\Framework\TestCase { /** * Tear down after each test @@ -43,13 +44,14 @@ class ParagraphTest extends \PHPUnit_Framework_TestCase $object = new Paragraph(); $attributes = array( - 'widowControl' => true, - 'keepNext' => false, - 'keepLines' => false, - 'pageBreakBefore' => false, + 'widowControl' => true, + 'keepNext' => false, + 'keepLines' => false, + 'pageBreakBefore' => false, + 'contextualSpacing' => false, ); foreach ($attributes as $key => $default) { - $get = "get{$key}"; + $get = $this->findGetter($key, $default, $object); $object->setStyleValue($key, null); $this->assertEquals($default, $object->$get()); $object->setStyleValue($key, ''); @@ -65,22 +67,27 @@ class ParagraphTest extends \PHPUnit_Framework_TestCase $object = new Paragraph(); $attributes = array( - 'spaceAfter' => 240, - 'spaceBefore' => 240, - 'indent' => 1, - 'hanging' => 1, - 'spacing' => 120, - 'basedOn' => 'Normal', - 'next' => 'Normal', - 'numStyle' => 'numStyle', - 'numLevel' => 1, - 'widowControl' => false, - 'keepNext' => true, - 'keepLines' => true, - 'pageBreakBefore' => true, + 'spaceAfter' => 240, + 'spaceBefore' => 240, + 'indent' => 1, + 'hanging' => 1, + 'spacing' => 120, + 'spacingLineRule' => LineSpacingRule::AT_LEAST, + 'basedOn' => 'Normal', + 'next' => 'Normal', + 'numStyle' => 'numStyle', + 'numLevel' => 1, + 'widowControl' => false, + 'keepNext' => true, + 'keepLines' => true, + 'pageBreakBefore' => true, + 'contextualSpacing' => true, + 'textAlignment' => 'auto', + 'bidi' => true, + 'suppressAutoHyphens' => true, ); foreach ($attributes as $key => $value) { - $get = "get{$key}"; + $get = $this->findGetter($key, $value, $object); $object->setStyleValue("$key", $value); if ('indent' == $key || 'hanging' == $key) { $value = $value * 720; @@ -91,6 +98,19 @@ class ParagraphTest extends \PHPUnit_Framework_TestCase } } + private function findGetter($key, $value, $object) + { + if (is_bool($value)) { + if (method_exists($object, "is{$key}")) { + return "is{$key}"; + } elseif (method_exists($object, "has{$key}")) { + return "has{$key}"; + } + } + + return "get{$key}"; + } + /** * Test get null style value */ @@ -98,9 +118,9 @@ class ParagraphTest extends \PHPUnit_Framework_TestCase { $object = new Paragraph(); - $attributes = array('spacing', 'indent', 'hanging', 'spaceBefore', 'spaceAfter'); + $attributes = array('spacing', 'indent', 'hanging', 'spaceBefore', 'spaceAfter', 'textAlignment'); foreach ($attributes as $key) { - $get = "get{$key}"; + $get = $this->findGetter($key, null, $object); $this->assertNull($object->$get()); } } diff --git a/tests/PhpWord/Style/RowTest.php b/tests/PhpWord/Style/RowTest.php index a89f73d2..534815b1 100644 --- a/tests/PhpWord/Style/RowTest.php +++ b/tests/PhpWord/Style/RowTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Style\Row * @runTestsInSeparateProcesses */ -class RowTest extends \PHPUnit_Framework_TestCase +class RowTest extends \PHPUnit\Framework\TestCase { /** * Test properties with boolean value diff --git a/tests/PhpWord/Style/SectionTest.php b/tests/PhpWord/Style/SectionTest.php index ed25ac36..b26d1d94 100644 --- a/tests/PhpWord/Style/SectionTest.php +++ b/tests/PhpWord/Style/SectionTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -23,7 +23,7 @@ namespace PhpOffice\PhpWord\Style; * @coversDefaultClass \PhpOffice\PhpWord\Element\Section * @runTestsInSeparateProcesses */ -class SettingsTest extends \PHPUnit_Framework_TestCase +class SectionTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -33,14 +33,14 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $oSettings = new Section(); $this->assertEquals('portrait', $oSettings->getOrientation()); - $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW()); - $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH()); + $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW(), '', 0.000000001); + $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH(), '', 0.000000001); $this->assertEquals('A4', $oSettings->getPaperSize()); $oSettings->setSettingValue('orientation', 'landscape'); $this->assertEquals('landscape', $oSettings->getOrientation()); - $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeW()); - $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeH()); + $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeW(), '', 0.000000001); + $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeH(), '', 0.000000001); $iVal = rand(1, 1000); $oSettings->setSettingValue('borderSize', $iVal); @@ -110,7 +110,7 @@ class SettingsTest extends \PHPUnit_Framework_TestCase // Section Settings $oSettings = new Section(); - $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW()); + $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW(), '', 0.000000001); $iVal = rand(1, 1000); $oSettings->setSettingValue('pageSizeW', $iVal); $this->assertEquals($iVal, $oSettings->getPageSizeW()); @@ -124,7 +124,7 @@ class SettingsTest extends \PHPUnit_Framework_TestCase // Section Settings $oSettings = new Section(); - $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH()); + $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH(), '', 0.000000001); $iVal = rand(1, 1000); $oSettings->setSettingValue('pageSizeH', $iVal); $this->assertEquals($iVal, $oSettings->getPageSizeH()); @@ -140,8 +140,8 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $oSettings->setLandscape(); $this->assertEquals('landscape', $oSettings->getOrientation()); - $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeW()); - $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeH()); + $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeW(), '', 0.000000001); + $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeH(), '', 0.000000001); } /** @@ -154,8 +154,8 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $oSettings->setPortrait(); $this->assertEquals('portrait', $oSettings->getOrientation()); - $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW()); - $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH()); + $this->assertEquals(Section::DEFAULT_WIDTH, $oSettings->getPageSizeW(), '', 0.000000001); + $this->assertEquals(Section::DEFAULT_HEIGHT, $oSettings->getPageSizeH(), '', 0.000000001); } /** diff --git a/tests/PhpWord/Style/ShadingTest.php b/tests/PhpWord/Style/ShadingTest.php index d6378f8d..7aba03a1 100644 --- a/tests/PhpWord/Style/ShadingTest.php +++ b/tests/PhpWord/Style/ShadingTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\Shading */ -class ShadingTest extends \PHPUnit_Framework_TestCase +class ShadingTest extends \PHPUnit\Framework\TestCase { /** * Test get/set diff --git a/tests/PhpWord/Style/SpacingTest.php b/tests/PhpWord/Style/SpacingTest.php index 79c9e458..f7402edd 100644 --- a/tests/PhpWord/Style/SpacingTest.php +++ b/tests/PhpWord/Style/SpacingTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\Spacing */ -class SpacingTest extends \PHPUnit_Framework_TestCase +class SpacingTest extends \PHPUnit\Framework\TestCase { /** * Test get/set @@ -31,10 +31,10 @@ class SpacingTest extends \PHPUnit_Framework_TestCase { $object = new Spacing(); $properties = array( - 'before' => array(null, 10), - 'after' => array(null, 10), - 'line' => array(null, 10), - 'rule' => array('auto', 'exact'), + 'before' => array(null, 10), + 'after' => array(null, 10), + 'line' => array(null, 10), + 'lineRule' => array('auto', 'exact'), ); foreach ($properties as $property => $value) { list($default, $expected) = $value; diff --git a/tests/PhpWord/Style/TOCTest.php b/tests/PhpWord/Style/TOCTest.php index 03620c17..445c5e43 100644 --- a/tests/PhpWord/Style/TOCTest.php +++ b/tests/PhpWord/Style/TOCTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\TOC */ -class TOCTest extends \PHPUnit_Framework_TestCase +class TOCTest extends \PHPUnit\Framework\TestCase { /** * Test get/set diff --git a/tests/PhpWord/Style/TabTest.php b/tests/PhpWord/Style/TabTest.php index 7724aa41..8d8d3f6c 100644 --- a/tests/PhpWord/Style/TabTest.php +++ b/tests/PhpWord/Style/TabTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Style; * * @coversDefaultClass \PhpOffice\PhpWord\Style\Tab */ -class TabTest extends \PHPUnit_Framework_TestCase +class TabTest extends \PHPUnit\Framework\TestCase { /** * Test get/set diff --git a/tests/PhpWord/Style/TablePositionTest.php b/tests/PhpWord/Style/TablePositionTest.php new file mode 100644 index 00000000..1243c3d5 --- /dev/null +++ b/tests/PhpWord/Style/TablePositionTest.php @@ -0,0 +1,65 @@ + TablePosition::VANCHOR_PAGE, 'bottomFromText' => 20); + + $object = new TablePosition($styleTable); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $object->getVertAnchor()); + $this->assertEquals(20, $object->getBottomFromText()); + } + + /** + * Test setting style with normal value + */ + public function testSetGetNormal() + { + $object = new TablePosition(); + + $attributes = array( + 'leftFromText' => 4, + 'rightFromText' => 4, + 'topFromText' => 4, + 'bottomFromText' => 4, + 'vertAnchor' => TablePosition::VANCHOR_PAGE, + 'horzAnchor' => TablePosition::HANCHOR_TEXT, + 'tblpXSpec' => TablePosition::XALIGN_CENTER, + 'tblpX' => 5, + 'tblpYSpec' => TablePosition::YALIGN_OUTSIDE, + 'tblpY' => 6, + ); + foreach ($attributes as $key => $value) { + $set = "set{$key}"; + $get = "get{$key}"; + $object->$set($value); + $this->assertEquals($value, $object->$get()); + } + } +} diff --git a/tests/PhpWord/Style/TableTest.php b/tests/PhpWord/Style/TableTest.php index 5e878692..91fc3550 100644 --- a/tests/PhpWord/Style/TableTest.php +++ b/tests/PhpWord/Style/TableTest.php @@ -10,21 +10,23 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType; use PhpOffice\PhpWord\SimpleType\JcTable; +use PhpOffice\PhpWord\SimpleType\TblWidth; /** * Test class for PhpOffice\PhpWord\Style\Table * * @runTestsInSeparateProcesses */ -class TableTest extends \PHPUnit_Framework_TestCase +class TableTest extends \PHPUnit\Framework\TestCase { /** * Test class construction @@ -38,9 +40,6 @@ class TableTest extends \PHPUnit_Framework_TestCase $styleTable = array('bgColor' => 'FF0000'); $styleFirstRow = array('borderBottomSize' => 3); - $object = new Table(); - $this->assertNull($object->getBgColor()); - $object = new Table($styleTable, $styleFirstRow); $this->assertEquals('FF0000', $object->getBgColor()); @@ -49,6 +48,19 @@ class TableTest extends \PHPUnit_Framework_TestCase $this->assertEquals(3, $firstRow->getBorderBottomSize()); } + /** + * Test default values when passing no style + */ + public function testDefaultValues() + { + $object = new Table(); + + $this->assertNull($object->getBgColor()); + $this->assertEquals(Table::LAYOUT_AUTO, $object->getLayout()); + $this->assertEquals(TblWidth::AUTO, $object->getUnit()); + $this->assertNull($object->getIndent()); + } + /** * Test setting style with normal value */ @@ -77,6 +89,7 @@ class TableTest extends \PHPUnit_Framework_TestCase 'alignment' => JcTable::CENTER, 'width' => 100, 'unit' => 'pct', + 'layout' => Table::LAYOUT_FIXED, ); foreach ($attributes as $key => $value) { $set = "set{$key}"; @@ -99,6 +112,7 @@ class TableTest extends \PHPUnit_Framework_TestCase $value = 'FF0000'; $object->setBorderColor($value); + $values = array(); foreach ($parts as $part) { $get = "getBorder{$part}Color"; $values[] = $value; @@ -121,6 +135,7 @@ class TableTest extends \PHPUnit_Framework_TestCase $value = 4; $object->setBorderSize($value); + $values = array(); foreach ($parts as $part) { $get = "getBorder{$part}Size"; $values[] = $value; @@ -143,6 +158,7 @@ class TableTest extends \PHPUnit_Framework_TestCase $value = 240; $object->setCellMargin($value); + $values = array(); foreach ($parts as $part) { $get = "getCellMargin{$part}"; $values[] = $value; @@ -169,4 +185,38 @@ class TableTest extends \PHPUnit_Framework_TestCase $object->getBorderColor() ); } + + /** + * Tests table cell spacing + */ + public function testTableCellSpacing() + { + $object = new Table(); + $this->assertNull($object->getCellSpacing()); + + $object = new Table(array('cellSpacing' => 20)); + $this->assertEquals(20, $object->getCellSpacing()); + } + + /** + * Tests table floating position + */ + public function testTablePosition() + { + $object = new Table(); + $this->assertNull($object->getPosition()); + + $object->setPosition(array('vertAnchor' => TablePosition::VANCHOR_PAGE)); + $this->assertNotNull($object->getPosition()); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $object->getPosition()->getVertAnchor()); + } + + public function testIndent() + { + $indent = new TblWidthComplexType(100, TblWidth::TWIP); + + $table = new Table(array('indent' => $indent)); + + $this->assertSame($indent, $table->getIndent()); + } } diff --git a/tests/PhpWord/Style/TextBoxTest.php b/tests/PhpWord/Style/TextBoxTest.php index ea7bc71f..803189cd 100644 --- a/tests/PhpWord/Style/TextBoxTest.php +++ b/tests/PhpWord/Style/TextBoxTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * @coversDefaultClass \PhpOffice\PhpWord\Style\Image * @runTestsInSeparateProcesses */ -class TextBoxTest extends \PHPUnit_Framework_TestCase +class TextBoxTest extends \PHPUnit\Framework\TestCase { /** * Test setting style with normal value @@ -238,7 +238,6 @@ class TextBoxTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $object->getPosVerticalRel()); } - /** * Test set/get innerMarginRight */ diff --git a/tests/PhpWord/StyleTest.php b/tests/PhpWord/StyleTest.php index 57ec98f4..cbc39c87 100644 --- a/tests/PhpWord/StyleTest.php +++ b/tests/PhpWord/StyleTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * @coversDefaultClass \PhpOffice\PhpWord\Style * @runTestsInSeparateProcesses */ -class StyleTest extends \PHPUnit_Framework_TestCase +class StyleTest extends \PHPUnit\Framework\TestCase { /** * Add and get paragraph, font, link, title, and table styles diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 11b43cf4..1513486e 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord; * @coversDefaultClass \PhpOffice\PhpWord\TemplateProcessor * @runTestsInSeparateProcesses */ -final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase +final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase { /** * Template can be saved in temporary location. @@ -36,7 +36,7 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase $templateProcessor = new TemplateProcessor($templateFqfn); $xslDomDocument = new \DOMDocument(); - $xslDomDocument->load(__DIR__ . "/_files/xsl/remove_tables_by_needle.xsl"); + $xslDomDocument->load(__DIR__ . '/_files/xsl/remove_tables_by_needle.xsl'); foreach (array('${employee.', '${scoreboard.', '${reference.') as $needle) { $templateProcessor->applyXslStyleSheet($xslDomDocument, array('needle' => $needle)); } @@ -223,4 +223,66 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase unlink($docName); $this->assertTrue($docFound); } + + /** + * @covers ::cloneBlock + * @test + */ + public function cloneBlockCanCloneABlockTwice() + { + // create template with placeholders and block + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $documentElements = array( + 'Title: ${title}', + '${subreport}', + '${subreport.id}: ${subreport.text}. ', + '${/subreport}', + ); + foreach ($documentElements as $documentElement) { + $section->addText($documentElement); + } + $objWriter = IOFactory::createWriter($phpWord); + $templatePath = 'test.docx'; + $objWriter->save($templatePath); + + // replace placeholders and save the file + $templateProcessor = new TemplateProcessor($templatePath); + $templateProcessor->setValue('title', 'Some title'); + $templateProcessor->cloneBlock('subreport', 2); + $templateProcessor->setValue('subreport.id', '123', 1); + $templateProcessor->setValue('subreport.text', 'Some text', 1); + $templateProcessor->setValue('subreport.id', '456', 1); + $templateProcessor->setValue('subreport.text', 'Some other text', 1); + $templateProcessor->saveAs($templatePath); + + // assert the block has been cloned twice + // and the placeholders have been replaced correctly + $phpWord = IOFactory::load($templatePath); + $sections = $phpWord->getSections(); + /** @var \PhpOffice\PhpWord\Element\TextRun[] $actualElements */ + $actualElements = $sections[0]->getElements(); + unlink($templatePath); + $expectedElements = array( + 'Title: Some title', + '123: Some text. ', + '456: Some other text. ', + ); + $this->assertCount(count($expectedElements), $actualElements); + foreach ($expectedElements as $i => $expectedElement) { + $this->assertEquals( + $expectedElement, + $actualElements[$i]->getElement(0)->getText() + ); + } + } + + public function testMainPartNameDetection() + { + $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/document22-xml.docx'); + + $variables = array('test'); + + $this->assertEquals($variables, $templateProcessor->getVariables()); + } } diff --git a/tests/PhpWord/Writer/HTML/ElementTest.php b/tests/PhpWord/Writer/HTML/ElementTest.php index 2a1e03dc..5b7176c7 100644 --- a/tests/PhpWord/Writer/HTML/ElementTest.php +++ b/tests/PhpWord/Writer/HTML/ElementTest.php @@ -10,27 +10,31 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\HTML; use PhpOffice\PhpWord\Element\Text as TextElement; +use PhpOffice\PhpWord\Element\TextRun; +use PhpOffice\PhpWord\Element\TrackChange; +use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Writer\HTML; use PhpOffice\PhpWord\Writer\HTML\Element\Text; /** * Test class for PhpOffice\PhpWord\Writer\HTML\Element subnamespace */ -class ElementTest extends \PHPUnit_Framework_TestCase +class ElementTest extends \PHPUnit\Framework\TestCase { /** * Test unmatched elements */ public function testUnmatchedElements() { - $elements = array('Container', 'Footnote', 'Image', 'Link', 'ListItem', 'Table', 'Title'); + $elements = array('Container', 'Footnote', 'Image', 'Link', 'ListItem', 'Table', 'Title', 'Bookmark'); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\HTML\\Element\\' . $element; $parentWriter = new HTML(); @@ -53,4 +57,128 @@ class ElementTest extends \PHPUnit_Framework_TestCase $this->assertEquals(htmlspecialchars('-A-', ENT_COMPAT, 'UTF-8'), $object->write()); } + + /** + * Test write TrackChange + */ + public function testWriteTrackChanges() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $text = $section->addText('my dummy text'); + $text->setChangeInfo(TrackChange::INSERTED, 'author name'); + $text2 = $section->addText('my other text'); + $text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new \DateTime())); + + $dom = $this->getAsHTML($phpWord); + $xpath = new \DOMXPath($dom); + + $this->assertEquals(1, $xpath->query('/html/body/p[1]/ins')->length); + $this->assertEquals(1, $xpath->query('/html/body/p[2]/del')->length); + } + + /** + * Tests writing table with col span + */ + public function testWriteColSpan() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable(); + $row1 = $table->addRow(); + $cell11 = $row1->addCell(1000, array('gridSpan' => 2)); + $cell11->addText('cell spanning 2 bellow'); + $row2 = $table->addRow(); + $cell21 = $row2->addCell(500); + $cell21->addText('first cell'); + $cell22 = $row2->addCell(500); + $cell22->addText('second cell'); + + $dom = $this->getAsHTML($phpWord); + $xpath = new \DOMXPath($dom); + + $this->assertEquals(1, $xpath->query('/html/body/table/tr[1]/td')->length); + $this->assertEquals('2', $xpath->query('/html/body/table/tr/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent); + $this->assertEquals(2, $xpath->query('/html/body/table/tr[2]/td')->length); + } + + /** + * Tests writing table with row span + */ + public function testWriteRowSpan() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable(); + + $row1 = $table->addRow(); + $row1->addCell(1000, array('vMerge' => 'restart'))->addText('row spanning 3 bellow'); + $row1->addCell(500)->addText('first cell being spanned'); + + $row2 = $table->addRow(); + $row2->addCell(null, array('vMerge' => 'continue')); + $row2->addCell(500)->addText('second cell being spanned'); + + $row3 = $table->addRow(); + $row3->addCell(null, array('vMerge' => 'continue')); + $row3->addCell(500)->addText('third cell being spanned'); + + $dom = $this->getAsHTML($phpWord); + $xpath = new \DOMXPath($dom); + + $this->assertEquals(2, $xpath->query('/html/body/table/tr[1]/td')->length); + $this->assertEquals('3', $xpath->query('/html/body/table/tr[1]/td[1]')->item(0)->attributes->getNamedItem('rowspan')->textContent); + $this->assertEquals(1, $xpath->query('/html/body/table/tr[2]/td')->length); + } + + private function getAsHTML(PhpWord $phpWord) + { + $htmlWriter = new HTML($phpWord); + $dom = new \DOMDocument(); + $dom->loadHTML($htmlWriter->getContent()); + + return $dom; + } + + public function testWriteTitleTextRun() + { + $expected = 'Title with TextRun'; + + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $textRun = new TextRun(); + $textRun->addText($expected); + + $section->addTitle($textRun); + + $htmlWriter = new HTML($phpWord); + $content = $htmlWriter->getContent(); + + $this->assertContains($expected, $content); + } + + /** + * Tests writing table with layout + */ + public function testWriteTableLayout() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->addTable(); + + $table1 = $section->addTable(array('layout' => \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED)); + $row1 = $table1->addRow(); + $row1->addCell()->addText('fixed layout table'); + + $table2 = $section->addTable(array('layout' => \PhpOffice\PhpWord\Style\Table::LAYOUT_AUTO)); + $row2 = $table2->addRow(); + $row2->addCell()->addText('auto layout table'); + + $dom = $this->getAsHTML($phpWord); + $xpath = new \DOMXPath($dom); + + $this->assertEquals('table-layout: fixed;', $xpath->query('/html/body/table[1]')->item(0)->attributes->getNamedItem('style')->textContent); + $this->assertEquals('table-layout: auto;', $xpath->query('/html/body/table[2]')->item(0)->attributes->getNamedItem('style')->textContent); + } } diff --git a/tests/PhpWord/Writer/HTML/PartTest.php b/tests/PhpWord/Writer/HTML/PartTest.php index 137a092e..f8303414 100644 --- a/tests/PhpWord/Writer/HTML/PartTest.php +++ b/tests/PhpWord/Writer/HTML/PartTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\HTML; use PhpOffice\PhpWord\Writer\HTML\Part\Body; @@ -21,7 +22,7 @@ use PhpOffice\PhpWord\Writer\HTML\Part\Body; /** * Test class for PhpOffice\PhpWord\Writer\HTML\Part subnamespace */ -class PartTest extends \PHPUnit_Framework_TestCase +class PartTest extends \PHPUnit\Framework\TestCase { /** * Test get parent writer exception diff --git a/tests/PhpWord/Writer/HTML/StyleTest.php b/tests/PhpWord/Writer/HTML/StyleTest.php index 629efd7a..5b60226c 100644 --- a/tests/PhpWord/Writer/HTML/StyleTest.php +++ b/tests/PhpWord/Writer/HTML/StyleTest.php @@ -10,16 +10,17 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\HTML; /** * Test class for PhpOffice\PhpWord\Writer\HTML\Style subnamespace */ -class StyleTest extends \PHPUnit_Framework_TestCase +class StyleTest extends \PHPUnit\Framework\TestCase { /** * Test empty styles diff --git a/tests/PhpWord/Writer/HTMLTest.php b/tests/PhpWord/Writer/HTMLTest.php index b2b10165..8868db5a 100644 --- a/tests/PhpWord/Writer/HTMLTest.php +++ b/tests/PhpWord/Writer/HTMLTest.php @@ -10,13 +10,15 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\SimpleType\Jc; /** @@ -24,14 +26,14 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class HTMLTest extends \PHPUnit_Framework_TestCase +class HTMLTest extends \PHPUnit\Framework\TestCase { /** * Construct */ public function testConstruct() { - $object = new HTML(new PhpWord); + $object = new HTML(new PhpWord()); $this->assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $object->getPhpWord()); } @@ -71,6 +73,7 @@ class HTMLTest extends \PHPUnit_Framework_TestCase ); $phpWord->addParagraphStyle('Paragraph', array('alignment' => Jc::CENTER, 'spaceAfter' => 20, 'spaceBefore' => 20)); $section = $phpWord->addSection(); + $section->addBookmark('top'); $section->addText(htmlspecialchars('Test 1', ENT_COMPAT, 'UTF-8'), 'Font', 'Paragraph'); $section->addTextBreak(); $section->addText( @@ -94,6 +97,15 @@ class HTMLTest extends \PHPUnit_Framework_TestCase $textrun->addText(htmlspecialchars('Test 3', ENT_COMPAT, 'UTF-8')); $textrun->addTextBreak(); + $textrun = $section->addTextRun(array('alignment' => Jc::START)); + $textrun->addText(htmlspecialchars('Text left aligned', ENT_COMPAT, 'UTF-8')); + + $textrun = $section->addTextRun(array('alignment' => Jc::BOTH)); + $textrun->addText(htmlspecialchars('Text justified', ENT_COMPAT, 'UTF-8')); + + $textrun = $section->addTextRun(array('alignment' => Jc::END)); + $textrun->addText(htmlspecialchars('Text right aligned', ENT_COMPAT, 'UTF-8')); + $textrun = $section->addTextRun('Paragraph'); $textrun->addLink('https://github.com/PHPOffice/PHPWord'); $textrun->addImage($localImage); @@ -117,12 +129,17 @@ class HTMLTest extends \PHPUnit_Framework_TestCase $cell->addFootnote(); $cell->addEndnote(); $cell = $table->addRow()->addCell(); + $section->addLink('top', 'back to top', null, null, true); $writer = new HTML($phpWord); + $writer->save($file); + $this->assertFileExists($file); + unlink($file); - $this->assertTrue(file_exists($file)); - + Settings::setOutputEscapingEnabled(true); + $writer->save($file); + $this->assertFileExists($file); unlink($file); } } diff --git a/tests/PhpWord/Writer/ODText/ElementTest.php b/tests/PhpWord/Writer/ODText/ElementTest.php index fb14aae5..37f0d1ef 100644 --- a/tests/PhpWord/Writer/ODText/ElementTest.php +++ b/tests/PhpWord/Writer/ODText/ElementTest.php @@ -10,18 +10,21 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\ODText; use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\TestHelperDOCX; /** * Test class for PhpOffice\PhpWord\Writer\ODText\Element subnamespace */ -class ElementTest extends \PHPUnit_Framework_TestCase +class ElementTest extends \PHPUnit\Framework\TestCase { /** * Test unmatched elements @@ -39,4 +42,21 @@ class ElementTest extends \PHPUnit_Framework_TestCase $this->assertEquals('', $xmlWriter->getData()); } } + + /** + * Test PageBreak + */ + public function testPageBreak() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->addText('test'); + $section->addPageBreak(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $element = '/office:document-content/office:body/office:text/text:section/text:p[2]'; + $this->assertTrue($doc->elementExists($element, 'content.xml')); + $this->assertEquals('P1', $doc->getElementAttribute($element, 'text:style-name', 'content.xml')); + } } diff --git a/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php b/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php index 90874b47..51d893d2 100644 --- a/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php +++ b/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\PhpWord\Writer\ODText; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\Writer\ODText; * @coversDefaultClass \PhpOffice\PhpWord\Writer\ODText\Part\AbstractPart * @runTestsInSeparateProcesses */ -class AbstractPartTest extends \PHPUnit_Framework_TestCase +class AbstractPartTest extends \PHPUnit\Framework\TestCase { /** * covers ::setParentWriter @@ -40,7 +41,7 @@ class AbstractPartTest extends \PHPUnit_Framework_TestCase /** * covers ::getParentWriter * - * @expectedException Exception + * @expectedException \Exception * @expectedExceptionMessage No parent WriterInterface assigned. */ public function testSetGetParentWriterNull() diff --git a/tests/PhpWord/Writer/ODText/Part/ContentTest.php b/tests/PhpWord/Writer/ODText/Part/ContentTest.php index 5814fa60..2e501c60 100644 --- a/tests/PhpWord/Writer/ODText/Part/ContentTest.php +++ b/tests/PhpWord/Writer/ODText/Part/ContentTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\PhpWord\PhpWord; @@ -26,7 +27,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * @coversDefaultClass \PhpOffice\PhpWord\Writer\ODText\Part\Content * @runTestsInSeparateProcesses */ -class ContentTest extends \PHPUnit_Framework_TestCase +class ContentTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -82,6 +83,7 @@ class ContentTest extends \PHPUnit_Framework_TestCase $cell->addObject($objectSrc); $textrun = $cell->addTextRun(); $textrun->addText('Test text run'); + $section->addPageBreak(); $footer = $section->addFooter(); $footer->addPreserveText('{PAGE}'); diff --git a/tests/PhpWord/Writer/ODText/StyleTest.php b/tests/PhpWord/Writer/ODText/StyleTest.php index 6b979385..b1bf417d 100644 --- a/tests/PhpWord/Writer/ODText/StyleTest.php +++ b/tests/PhpWord/Writer/ODText/StyleTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\ODText; use PhpOffice\Common\XMLWriter; @@ -21,7 +22,7 @@ use PhpOffice\Common\XMLWriter; /** * Test class for PhpOffice\PhpWord\Writer\ODText\Style subnamespace */ -class StyleTest extends \PHPUnit_Framework_TestCase +class StyleTest extends \PHPUnit\Framework\TestCase { /** * Test empty styles diff --git a/tests/PhpWord/Writer/ODTextTest.php b/tests/PhpWord/Writer/ODTextTest.php index d79a9d42..a576a68d 100644 --- a/tests/PhpWord/Writer/ODTextTest.php +++ b/tests/PhpWord/Writer/ODTextTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\PhpWord; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class ODTextTest extends \PHPUnit_Framework_TestCase +class ODTextTest extends \PHPUnit\Framework\TestCase { /** * Construct @@ -90,7 +91,7 @@ class ODTextTest extends \PHPUnit_Framework_TestCase $writer = new ODText($phpWord); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } @@ -102,11 +103,14 @@ class ODTextTest extends \PHPUnit_Framework_TestCase */ public function testSavePhpOutput() { + $this->setOutputCallback(function () { + }); $phpWord = new PhpWord(); $section = $phpWord->addSection(); $section->addText('Test'); $writer = new ODText($phpWord); $writer->save('php://output'); + $this->assertNotNull($this->getActualOutput()); } /** @@ -136,7 +140,7 @@ class ODTextTest extends \PHPUnit_Framework_TestCase */ public function testSetUseDiskCachingException() { - $dir = join(DIRECTORY_SEPARATOR, array(PHPWORD_TESTS_BASE_DIR, 'foo')); + $dir = implode(DIRECTORY_SEPARATOR, array(PHPWORD_TESTS_BASE_DIR, 'foo')); $object = new ODText(); $object->setUseDiskCaching(true, $dir); diff --git a/tests/PhpWord/Writer/PDF/DomPDFTest.php b/tests/PhpWord/Writer/PDF/DomPDFTest.php index 67026a84..bc229d51 100644 --- a/tests/PhpWord/Writer/PDF/DomPDFTest.php +++ b/tests/PhpWord/Writer/PDF/DomPDFTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\PDF; use PhpOffice\PhpWord\PhpWord; @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\Writer\PDF; * * @runTestsInSeparateProcesses */ -class DomPDFTest extends \PHPUnit_Framework_TestCase +class DomPDFTest extends \PHPUnit\Framework\TestCase { /** * Test construct @@ -45,7 +46,7 @@ class DomPDFTest extends \PHPUnit_Framework_TestCase $writer = new PDF($phpWord); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } diff --git a/tests/PhpWord/Writer/PDF/MPDFTest.php b/tests/PhpWord/Writer/PDF/MPDFTest.php index b6c85a40..34a5f8d8 100644 --- a/tests/PhpWord/Writer/PDF/MPDFTest.php +++ b/tests/PhpWord/Writer/PDF/MPDFTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\PDF; use PhpOffice\PhpWord\PhpWord; @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\Writer\PDF; * * @runTestsInSeparateProcesses */ -class MPDFTest extends \PHPUnit_Framework_TestCase +class MPDFTest extends \PHPUnit\Framework\TestCase { /** * Test construct @@ -37,6 +38,7 @@ class MPDFTest extends \PHPUnit_Framework_TestCase $phpWord = new PhpWord(); $section = $phpWord->addSection(); $section->addText('Test 1'); + $section->addPageBreak(); $rendererName = Settings::PDF_RENDERER_MPDF; $rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/mpdf/mpdf'); @@ -44,7 +46,7 @@ class MPDFTest extends \PHPUnit_Framework_TestCase $writer = new PDF($phpWord); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } diff --git a/tests/PhpWord/Writer/PDF/TCPDFTest.php b/tests/PhpWord/Writer/PDF/TCPDFTest.php index aaec55eb..6dc8f24c 100644 --- a/tests/PhpWord/Writer/PDF/TCPDFTest.php +++ b/tests/PhpWord/Writer/PDF/TCPDFTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\PDF; use PhpOffice\PhpWord\PhpWord; @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\Writer\PDF; * * @runTestsInSeparateProcesses */ -class TCPDFTest extends \PHPUnit_Framework_TestCase +class TCPDFTest extends \PHPUnit\Framework\TestCase { /** * Test construct @@ -44,7 +45,7 @@ class TCPDFTest extends \PHPUnit_Framework_TestCase $writer = new PDF($phpWord); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } diff --git a/tests/PhpWord/Writer/PDFTest.php b/tests/PhpWord/Writer/PDFTest.php index 75db6c03..f699385c 100644 --- a/tests/PhpWord/Writer/PDFTest.php +++ b/tests/PhpWord/Writer/PDFTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\PhpWord; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\Settings; * * @runTestsInSeparateProcesses */ -class PDFTest extends \PHPUnit_Framework_TestCase +class PDFTest extends \PHPUnit\Framework\TestCase { /** * Test normal construct @@ -40,7 +41,7 @@ class PDFTest extends \PHPUnit_Framework_TestCase $writer = new PDF(new PhpWord()); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 47d01d00..47630335 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\RTF; use PhpOffice\PhpWord\Writer\RTF; @@ -21,7 +22,7 @@ use PhpOffice\PhpWord\Writer\RTF; /** * Test class for PhpOffice\PhpWord\Writer\RTF\Element subnamespace */ -class ElementTest extends \PHPUnit_Framework_TestCase +class ElementTest extends \PHPUnit\Framework\TestCase { /** * Test unmatched elements diff --git a/tests/PhpWord/Writer/RTF/StyleTest.php b/tests/PhpWord/Writer/RTF/StyleTest.php index 095d30d5..317014c6 100644 --- a/tests/PhpWord/Writer/RTF/StyleTest.php +++ b/tests/PhpWord/Writer/RTF/StyleTest.php @@ -10,23 +10,28 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\RTF; +use PhpOffice\PhpWord\Writer\RTF; +use PhpOffice\PhpWord\Writer\RTF\Style\Border; +use PHPUnit\Framework\Assert; + /** * Test class for PhpOffice\PhpWord\Writer\RTF\Style subnamespace */ -class StyleTest extends \PHPUnit_Framework_TestCase +class StyleTest extends \PHPUnit\Framework\TestCase { /** * Test empty styles */ public function testEmptyStyles() { - $styles = array('Font', 'Paragraph', 'Section'); + $styles = array('Font', 'Paragraph', 'Section', 'Tab', 'Indentation'); foreach ($styles as $style) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\RTF\\Style\\' . $style; $object = new $objectClass(); @@ -34,4 +39,73 @@ class StyleTest extends \PHPUnit_Framework_TestCase $this->assertEquals('', $object->write()); } } + + public function testBorderWithNonRegisteredColors() + { + $border = new Border(); + $border->setSizes(array(1, 2, 3, 4)); + $border->setColors(array('#FF0000', '#FF0000', '#FF0000', '#FF0000')); + $border->setSizes(array(20, 20, 20, 20)); + + $content = $border->write(); + + $expected = '\pgbrdropt32'; + $expected .= '\pgbrdrt\brdrs\brdrw20\brdrcf0\brsp480 '; + $expected .= '\pgbrdrl\brdrs\brdrw20\brdrcf0\brsp480 '; + $expected .= '\pgbrdrr\brdrs\brdrw20\brdrcf0\brsp480 '; + $expected .= '\pgbrdrb\brdrs\brdrw20\brdrcf0\brsp480 '; + + $this->assertEquals($expected, $content); + } + + public function testIndentation() + { + $indentation = new \PhpOffice\PhpWord\Style\Indentation(); + $indentation->setLeft(1); + $indentation->setRight(2); + $indentation->setFirstLine(3); + + $indentWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Indentation($indentation); + $indentWriter->setParentWriter(new RTF()); + $result = $indentWriter->write(); + + Assert::assertEquals('\fi3\li1\ri2 ', $result); + } + + public function testRightTab() + { + $tabRight = new \PhpOffice\PhpWord\Style\Tab(); + $tabRight->setType(\PhpOffice\PhpWord\Style\Tab::TAB_STOP_RIGHT); + $tabRight->setPosition(5); + + $tabWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Tab($tabRight); + $tabWriter->setParentWriter(new RTF()); + $result = $tabWriter->write(); + + Assert::assertEquals('\tqr\tx5', $result); + } + + public function testCenterTab() + { + $tabRight = new \PhpOffice\PhpWord\Style\Tab(); + $tabRight->setType(\PhpOffice\PhpWord\Style\Tab::TAB_STOP_CENTER); + + $tabWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Tab($tabRight); + $tabWriter->setParentWriter(new RTF()); + $result = $tabWriter->write(); + + Assert::assertEquals('\tqc\tx0', $result); + } + + public function testDecimalTab() + { + $tabRight = new \PhpOffice\PhpWord\Style\Tab(); + $tabRight->setType(\PhpOffice\PhpWord\Style\Tab::TAB_STOP_DECIMAL); + + $tabWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Tab($tabRight); + $tabWriter->setParentWriter(new RTF()); + $result = $tabWriter->write(); + + Assert::assertEquals('\tqdec\tx0', $result); + } } diff --git a/tests/PhpWord/Writer/RTFTest.php b/tests/PhpWord/Writer/RTFTest.php index 0b4f6b0f..010720bd 100644 --- a/tests/PhpWord/Writer/RTFTest.php +++ b/tests/PhpWord/Writer/RTFTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\PhpWord; @@ -24,14 +25,14 @@ use PhpOffice\PhpWord\SimpleType\Jc; * * @runTestsInSeparateProcesses */ -class RTFTest extends \PHPUnit_Framework_TestCase +class RTFTest extends \PHPUnit\Framework\TestCase { /** * Construct */ public function testConstruct() { - $object = new RTF(new PhpWord); + $object = new RTF(new PhpWord()); $this->assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $object->getPhpWord()); } @@ -91,7 +92,7 @@ class RTFTest extends \PHPUnit_Framework_TestCase $writer = new RTF($phpWord); $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); @unlink($file); } @@ -103,10 +104,13 @@ class RTFTest extends \PHPUnit_Framework_TestCase */ public function testSavePhpOutput() { + $this->setOutputCallback(function () { + }); $phpWord = new PhpWord(); $section = $phpWord->addSection(); $section->addText(htmlspecialchars('Test', ENT_COMPAT, 'UTF-8')); $writer = new RTF($phpWord); $writer->save('php://output'); + $this->assertNotNull($this->getActualOutput()); } } diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index 027ba86a..25c62ecc 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -10,20 +10,24 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007; use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Element\Comment; +use PhpOffice\PhpWord\Element\TextRun; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\TestHelperDOCX; /** * Test class for PhpOffice\PhpWord\Writer\Word2007\Element subnamespace */ -class ElementTest extends \PHPUnit_Framework_TestCase +class ElementTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -40,8 +44,8 @@ class ElementTest extends \PHPUnit_Framework_TestCase { $elements = array( 'CheckBox', 'Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun', - 'Object', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC', - 'Field', 'Line', 'Shape', 'Chart', 'FormField', 'SDT', + 'OLEObject', 'PreserveText', 'Table', 'Text', 'TextBox', 'TextBreak', 'Title', 'TOC', + 'Field', 'Line', 'Shape', 'Chart', 'FormField', 'SDT', 'Bookmark', ); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $element; @@ -69,6 +73,87 @@ class ElementTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists($element)); } + /** + * Test bookmark element + */ + public function testBookmark() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $section->addBookmark('test_bookmark'); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:bookmarkStart'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('test_bookmark', $doc->getElementAttribute($element, 'w:name')); + + $element = '/w:document/w:body/w:bookmarkEnd'; + $this->assertTrue($doc->elementExists($element)); + } + + /** + * Test link element + */ + public function testLinkElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $section->addLink('https://github.com/PHPOffice/PHPWord'); + $section->addLink('internal_link', null, null, null, true); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p[1]/w:hyperlink/w:r/w:t'; + $this->assertTrue($doc->elementExists($element)); + + $element = '/w:document/w:body/w:p[2]/w:hyperlink/w:r/w:t'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('internal_link', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:hyperlink', 'w:anchor')); + } + + /** + * Basic test for table element + */ + public function testTableElements() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $table = $section->addTable(array('alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER)); + $table->addRow(900); + $table->addCell(2000)->addText('Row 1'); + $table->addCell(2000)->addText('Row 2'); + $table->addCell(2000)->addText('Row 3'); + $table->addCell(2000)->addText('Row 4'); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $tableRootElement = '/w:document/w:body/w:tbl'; + $this->assertTrue($doc->elementExists($tableRootElement . '/w:tblGrid/w:gridCol')); + $this->assertTrue($doc->elementExists($tableRootElement . '/w:tblPr/w:jc')); + $this->assertEquals('center', $doc->getElementAttribute($tableRootElement . '/w:tblPr/w:jc', 'w:val')); + } + + /** + * Tests that the style name gets added + */ + public function testTableWithStyleName() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $table = $section->addTable('my_predefined_style'); + $table->setWidth(75); + $table->addRow(900); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $tableRootElement = '/w:document/w:body/w:tbl'; + $this->assertTrue($doc->elementExists($tableRootElement . '/w:tblPr/w:tblStyle')); + $this->assertEquals('my_predefined_style', $doc->getElementAttribute($tableRootElement . '/w:tblPr/w:tblStyle', 'w:val')); + } + /** * Test shape elements */ @@ -171,7 +256,7 @@ class ElementTest extends \PHPUnit_Framework_TestCase { $phpWord = new PhpWord(); $section = $phpWord->addSection(); - $style = array('width' => 1000000, 'height' => 1000000); + $style = array('width' => 1000000, 'height' => 1000000, 'showAxisLabels' => true, 'showGridX' => true, 'showGridY' => true); $chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); $categories = array('A', 'B', 'C', 'D', 'E'); @@ -185,13 +270,90 @@ class ElementTest extends \PHPUnit_Framework_TestCase $index = 0; foreach ($chartTypes as $chartType) { - $index++; + ++$index; $file = "word/charts/chart{$index}.xml"; $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; $this->assertTrue($doc->elementExists($path, $file)); } } + public function testFieldElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $section->addField('INDEX', array(), array('\\c "3"')); + $section->addField('XE', array(), array('Bold', 'Italic'), 'Index Entry'); + $section->addField('DATE', array('dateformat' => 'd-M-yyyy'), array('PreserveFormat', 'LastUsedFormat')); + $section->addField('DATE', array(), array('LunarCalendar')); + $section->addField('DATE', array(), array('SakaEraCalendar')); + $section->addField('NUMPAGES', array('format' => 'roman', 'numformat' => '0,00'), array('SakaEraCalendar')); + $section->addField('STYLEREF', array('StyleIdentifier' => 'Heading 1')); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' INDEX \\c "3" ', $doc->getElement($element)->textContent); + } + + public function testFieldElementWithComplexText() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $text = new TextRun(); + $text->addText('test string', array('bold' => true)); + + $section->addField('XE', array(), array('Bold', 'Italic'), $text); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' XE "', $doc->getElement($element)->textContent); + + $element = '/w:document/w:body/w:p/w:r[3]/w:rPr/w:b'; + $this->assertTrue($doc->elementExists($element)); + + $element = '/w:document/w:body/w:p/w:r[3]/w:t'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('test string', $doc->getElement($element)->textContent); + + $element = '/w:document/w:body/w:p/w:r[4]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('"\\b \\i ', $doc->getElement($element)->textContent); + } + + /** + * Test writing the macrobutton field + */ + public function testMacroButtonField() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $macroText = new TextRun(); + $macroText->addText('Double click', array('bold' => true)); + $macroText->addText(' to '); + $macroText->addText('zoom to 100%', array('italic' => true)); + + $section->addField('MACROBUTTON', array('macroname' => 'Zoom100'), array(), $macroText); + $section->addField('MACROBUTTON', array('macroname' => 'Zoom100'), array(), 'double click to zoom'); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p[1]/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' MACROBUTTON Zoom100 ', $doc->getElement($element)->textContent); + + $element = '/w:document/w:body/w:p[1]/w:r[3]/'; + $this->assertTrue($doc->elementExists($element . 'w:t')); + $this->assertEquals('Double click', $doc->getElement($element . 'w:t')->textContent); + $this->assertTrue($doc->elementExists($element . 'w:rPr/w:b')); + + $element = '/w:document/w:body/w:p[2]/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' MACROBUTTON Zoom100 double click to zoom ', $doc->getElement($element)->textContent); + } + /** * Test form fields */ @@ -202,14 +364,16 @@ class ElementTest extends \PHPUnit_Framework_TestCase $section->addFormField('textinput')->setName('MyTextBox'); $section->addFormField('checkbox')->setDefault(true)->setValue('Your name'); + $section->addFormField('checkbox')->setDefault(true); $section->addFormField('dropdown')->setEntries(array('Choice 1', 'Choice 2', 'Choice 3')); $doc = TestHelperDOCX::getDocument($phpWord); - $path = '/w:document/w:body/w:p/w:r/w:fldChar/w:ffData'; - $this->assertTrue($doc->elementExists($path . '/w:textInput')); - $this->assertTrue($doc->elementExists($path . '/w:checkBox')); - $this->assertTrue($doc->elementExists($path . '/w:ddList')); + $path = '/w:document/w:body/w:p[%d]/w:r/w:fldChar/w:ffData'; + $this->assertTrue($doc->elementExists(sprintf($path, 1) . '/w:textInput')); + $this->assertTrue($doc->elementExists(sprintf($path, 2) . '/w:checkBox')); + $this->assertTrue($doc->elementExists(sprintf($path, 3) . '/w:checkBox')); + $this->assertTrue($doc->elementExists(sprintf($path, 4) . '/w:ddList')); } /** @@ -220,15 +384,112 @@ class ElementTest extends \PHPUnit_Framework_TestCase $phpWord = new PhpWord(); $section = $phpWord->addSection(); - $section->addSDT('comboBox'); + $section->addSDT('comboBox')->setListItems(array('1' => 'Choice 1', '2' => 'Choice 2'))->setValue('select value'); $section->addSDT('dropDownList'); - $section->addSDT('date'); + $section->addSDT('date')->setAlias('date_alias')->setTag('my_tag'); $doc = TestHelperDOCX::getDocument($phpWord); - $path = '/w:document/w:body/w:p/w:sdt/w:sdtPr'; - $this->assertTrue($doc->elementExists($path . '/w:comboBox')); - $this->assertTrue($doc->elementExists($path . '/w:dropDownList')); - $this->assertTrue($doc->elementExists($path . '/w:date')); + $path = '/w:document/w:body/w:p'; + + $this->assertTrue($doc->elementExists($path . '[1]/w:sdt/w:sdtContent/w:r/w:t')); + $this->assertEquals('select value', $doc->getElement($path . '[1]/w:sdt/w:sdtContent/w:r/w:t')->nodeValue); + $this->assertTrue($doc->elementExists($path . '[1]/w:sdt/w:sdtPr/w:comboBox')); + $this->assertTrue($doc->elementExists($path . '[1]/w:sdt/w:sdtPr/w:comboBox/w:listItem')); + $this->assertEquals('1', $doc->getElementAttribute($path . '[1]/w:sdt/w:sdtPr/w:comboBox/w:listItem[1]', 'w:value')); + $this->assertEquals('Choice 1', $doc->getElementAttribute($path . '[1]/w:sdt/w:sdtPr/w:comboBox/w:listItem[1]', 'w:displayText')); + + $this->assertTrue($doc->elementExists($path . '[2]/w:sdt/w:sdtPr/w:dropDownList')); + $this->assertFalse($doc->elementExists($path . '[2]/w:sdt/w:sdtPr/w:alias')); + + $this->assertTrue($doc->elementExists($path . '[3]/w:sdt/w:sdtPr/w:date')); + $this->assertTrue($doc->elementExists($path . '[3]/w:sdt/w:sdtPr/w:alias')); + $this->assertTrue($doc->elementExists($path . '[3]/w:sdt/w:sdtPr/w:tag')); + } + + /** + * Test Comment element + */ + public function testCommentWithoutEndElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $comment = new Comment('tester'); + $phpWord->addComment($comment); + + $element = $section->addText('this is a test'); + $element->setCommentRangeStart($comment); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:commentRangeStart')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:commentRangeEnd')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:commentReference')); + } + + /** + * Test Comment element + */ + public function testCommentWithEndElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $comment = new Comment('tester'); + $phpWord->addComment($comment); + + $element = $section->addText('this is a test'); + $element->setCommentRangeStart($comment); + $element->setCommentRangeEnd($comment); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:commentRangeStart')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:commentRangeEnd')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:commentReference')); + } + + /** + * Test Track changes + */ + public function testTrackChange() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $text = $section->addText('my dummy text'); + $text->setChangeInfo(TrackChange::INSERTED, 'author name'); + $text2 = $section->addText('my other text'); + $text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new \DateTime())); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:ins/w:r')); + $this->assertEquals('author name', $doc->getElementAttribute('/w:document/w:body/w:p/w:ins', 'w:author')); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:del/w:r/w:delText')); + } + + /** + * Test Title and Headings + */ + public function testTitleAndHeading() + { + $phpWord = new PhpWord(); + $phpWord->addTitleStyle(0, array('size' => 14, 'italic' => true)); + $phpWord->addTitleStyle(1, array('size' => 20, 'color' => '333333', 'bold' => true)); + + $section = $phpWord->addSection(); + $section->addTitle('This is a title', 0); + $section->addTitle('Heading 1', 1); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:r/w:t')); + $this->assertEquals('This is a title', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->textContent); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:pPr/w:pStyle')); + $this->assertEquals('Title', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:pStyle', 'w:val')); + + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:t')); + $this->assertEquals('Heading 1', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->textContent); + $this->assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:pPr/w:pStyle')); + $this->assertEquals('Heading1', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:pPr/w:pStyle', 'w:val')); } } diff --git a/tests/PhpWord/Writer/Word2007/Part/AbstractPartTest.php b/tests/PhpWord/Writer/Word2007/Part/AbstractPartTest.php index 8f72cdfe..fac94882 100644 --- a/tests/PhpWord/Writer/Word2007/Part/AbstractPartTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/AbstractPartTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\Writer\Word2007; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\Writer\Word2007; * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractWriterPart * @runTestsInSeparateProcesses */ -class AbstractWriterPartTest extends \PHPUnit_Framework_TestCase +class AbstractPartTest extends \PHPUnit\Framework\TestCase { /** * covers ::setParentWriter @@ -40,7 +41,7 @@ class AbstractWriterPartTest extends \PHPUnit_Framework_TestCase /** * covers ::getParentWriter * - * @expectedException Exception + * @expectedException \Exception * @expectedExceptionMessage No parent WriterInterface assigned. */ public function testSetGetParentWriterNull() diff --git a/tests/PhpWord/Writer/Word2007/Part/CommentsTest.php b/tests/PhpWord/Writer/Word2007/Part/CommentsTest.php new file mode 100644 index 00000000..0233abdf --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Part/CommentsTest.php @@ -0,0 +1,60 @@ +addText('Test'); + + $phpWord = new PhpWord(); + $phpWord->addComment($comment); + $doc = TestHelperDOCX::getDocument($phpWord); + + $path = '/w:comments/w:comment'; + $file = 'word/comments.xml'; + + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertNotNull($element->getAttribute('w:id')); + $this->assertEquals('Authors name', $element->getAttribute('w:author')); + $this->assertEquals('my_initials', $element->getAttribute('w:initials')); + } +} diff --git a/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php b/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php index a9e6d861..b35f9327 100644 --- a/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/DocumentTest.php @@ -10,14 +10,19 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; +use PhpOffice\PhpWord\ComplexType\FootnoteProperties; +use PhpOffice\PhpWord\Metadata\DocInfo; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\NumberFormat; +use PhpOffice\PhpWord\Style\Cell; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\TestHelperDOCX; @@ -26,7 +31,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * * @runTestsInSeparateProcesses */ -class DocumentTest extends \PHPUnit_Framework_TestCase +class DocumentTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -36,6 +41,32 @@ class DocumentTest extends \PHPUnit_Framework_TestCase TestHelperDOCX::clear(); } + /** + * Write custom properties + */ + public function testWriteCustomProps() + { + $phpWord = new PhpWord(); + $docInfo = $phpWord->getDocInfo(); + + $docInfo->setCustomProperty('key1', null); + $docInfo->setCustomProperty('key2', true); + $docInfo->setCustomProperty('key3', 3); + $docInfo->setCustomProperty('key4', 4.4); + $docInfo->setCustomProperty('key5', 'value5'); + $docInfo->setCustomProperty('key6', new \DateTime()); + $docInfo->setCustomProperty('key7', time(), DocInfo::PROPERTY_TYPE_DATE); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertNotNull($doc); + +// $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr')); +// $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool')); +// $this->assertTrue($doc->elementExists('/Properties/property[name="key3"]/vt:i4')); +// $this->assertTrue($doc->elementExists('/Properties/property[name="key4"]/vt:r8')); +// $this->assertTrue($doc->elementExists('/Properties/property[name="key5"]/vt:lpwstr')); + } + /** * Write end section page numbering */ @@ -57,6 +88,36 @@ class DocumentTest extends \PHPUnit_Framework_TestCase $this->assertEquals(2, $element->getAttribute('w:start')); } + /** + * Write section footnote properties + */ + public function testSectionFootnoteProperties() + { + $properties = new FootnoteProperties(); + $properties->setPos(FootnoteProperties::POSITION_DOC_END); + $properties->setNumFmt(NumberFormat::LOWER_ROMAN); + $properties->setNumStart(1); + $properties->setNumRestart(FootnoteProperties::RESTART_NUMBER_EACH_PAGE); + + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->setFootnoteProperties($properties); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = $doc->getElement('/w:document/w:body/w:sectPr/w:footnotePr/w:pos'); + $this->assertEquals(FootnoteProperties::POSITION_DOC_END, $element->getAttribute('w:val')); + + $element = $doc->getElement('/w:document/w:body/w:sectPr/w:footnotePr/w:numFmt'); + $this->assertEquals(NumberFormat::LOWER_ROMAN, $element->getAttribute('w:val')); + + $element = $doc->getElement('/w:document/w:body/w:sectPr/w:footnotePr/w:numStart'); + $this->assertEquals(1, $element->getAttribute('w:val')); + + $element = $doc->getElement('/w:document/w:body/w:sectPr/w:footnotePr/w:numRestart'); + $this->assertEquals(FootnoteProperties::RESTART_NUMBER_EACH_PAGE, $element->getAttribute('w:val')); + } + /** * Write elements */ @@ -426,7 +487,7 @@ class DocumentTest extends \PHPUnit_Framework_TestCase // Test the attributes $attributeCount = 0; foreach ($attributes as $key => $value) { - $attributeCount++; + ++$attributeCount; $nodeName = ($key == 'alignment') ? 'jc' : $key; $path = "/w:document/w:body/w:p[{$attributeCount}]/w:pPr/w:{$nodeName}"; if ('alignment' != $key) { @@ -473,6 +534,25 @@ class DocumentTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists("{$parent}/w:smallCaps")); } + /** + * Tests that if no color is set on a cell a border gets writen with the default color + */ + public function testWriteDefaultColor() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $cStyles['borderTopSize'] = 120; + + $table = $section->addTable(); + $table->addRow(); + $cell = $table->addCell(null, $cStyles); + $cell->addText('Test'); + + $doc = TestHelperDOCX::getDocument($phpWord); + $this->assertEquals(Cell::DEFAULT_BORDER_COLOR, $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr/w:tc/w:tcPr/w:tcBorders/w:top', 'w:color')); + } + /** * covers ::_writeTableStyle */ @@ -506,7 +586,7 @@ class DocumentTest extends \PHPUnit_Framework_TestCase $section = $phpWord->addSection(); $table = $section->addTable($tStyles); - $table->setWidth = 100; + $table->setWidth(100); $table->addRow($rHeight, $rStyles); $cell = $table->addCell($cWidth, $cStyles); $cell->addText('Test'); diff --git a/tests/PhpWord/Writer/Word2007/Part/FooterTest.php b/tests/PhpWord/Writer/Word2007/Part/FooterTest.php index 9a7d809a..1f9bba0d 100644 --- a/tests/PhpWord/Writer/Word2007/Part/FooterTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/FooterTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\Writer\Word2007; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\Writer\Word2007; * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Part\Footer * @runTestsInSeparateProcesses */ -class FooterTest extends \PHPUnit_Framework_TestCase +class FooterTest extends \PHPUnit\Framework\TestCase { /** * Write footer diff --git a/tests/PhpWord/Writer/Word2007/Part/FootnotesTest.php b/tests/PhpWord/Writer/Word2007/Part/FootnotesTest.php index 2d48fe36..4b0e94df 100644 --- a/tests/PhpWord/Writer/Word2007/Part/FootnotesTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/FootnotesTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\PhpWord; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * @coversNothing * @runTestsInSeparateProcesses */ -class FootnotesTest extends \PHPUnit_Framework_TestCase +class FootnotesTest extends \PHPUnit\Framework\TestCase { public function tearDown() { diff --git a/tests/PhpWord/Writer/Word2007/Part/HeaderTest.php b/tests/PhpWord/Writer/Word2007/Part/HeaderTest.php index 6c285af6..9edd0063 100644 --- a/tests/PhpWord/Writer/Word2007/Part/HeaderTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/HeaderTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\Writer\Word2007; @@ -23,7 +24,7 @@ use PhpOffice\PhpWord\Writer\Word2007; * * @runTestsInSeparateProcesses */ -class HeaderTest extends \PHPUnit_Framework_TestCase +class HeaderTest extends \PHPUnit\Framework\TestCase { /** * Write header diff --git a/tests/PhpWord/Writer/Word2007/Part/NumberingTest.php b/tests/PhpWord/Writer/Word2007/Part/NumberingTest.php index 9d11e5cb..fb5a220e 100644 --- a/tests/PhpWord/Writer/Word2007/Part/NumberingTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/NumberingTest.php @@ -10,14 +10,16 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\NumberFormat; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -27,7 +29,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * @runTestsInSeparateProcesses * @since 0.10.0 */ -class NumberingTest extends \PHPUnit_Framework_TestCase +class NumberingTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -52,7 +54,7 @@ class NumberingTest extends \PHPUnit_Framework_TestCase 'levels' => array( array( 'start' => 1, - 'format' => 'decimal', + 'format' => NumberFormat::DECIMAL, 'restart' => 1, 'suffix' => 'space', 'text' => '%1.', @@ -63,7 +65,7 @@ class NumberingTest extends \PHPUnit_Framework_TestCase 'font' => 'Arial', 'hint' => 'default', ), - ) + ), ) ); diff --git a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php index 6ed23e44..8a21e827 100644 --- a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php @@ -10,13 +10,19 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; +use PhpOffice\Common\Microsoft\PasswordEncoder; +use PhpOffice\PhpWord\ComplexType\ProofState; +use PhpOffice\PhpWord\ComplexType\TrackChangesView; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\SimpleType\Zoom; +use PhpOffice\PhpWord\Style\Language; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -24,7 +30,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Part\Settings */ -class SettingsTest extends \PHPUnit_Framework_TestCase +class SettingsTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -40,7 +46,7 @@ class SettingsTest extends \PHPUnit_Framework_TestCase public function testDocumentProtection() { $phpWord = new PhpWord(); - $phpWord->getProtection()->setEditing('forms'); + $phpWord->getSettings()->getDocumentProtection()->setEditing('forms'); $doc = TestHelperDOCX::getDocument($phpWord); @@ -50,6 +56,29 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists($path, $file)); } + /** + * Test document protection with password + */ + public function testDocumentProtectionWithPassword() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->getDocumentProtection()->setEditing('readOnly'); + $phpWord->getSettings()->getDocumentProtection()->setPassword('testÄö@€!$&'); + $phpWord->getSettings()->getDocumentProtection()->setSalt(base64_decode('uq81pJRRGFIY5U+E9gt8tA==')); + $phpWord->getSettings()->getDocumentProtection()->setAlgorithm(PasswordEncoder::ALGORITHM_MD2); + $phpWord->getSettings()->getDocumentProtection()->setSpinCount(10); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:documentProtection'; + $this->assertTrue($doc->elementExists($path, $file)); + $this->assertEquals('rUuJbk6LuN2/qFyp7IUPQA==', $doc->getElement($path, $file)->getAttribute('w:hash')); + $this->assertEquals('1', $doc->getElement($path, $file)->getAttribute('w:cryptAlgorithmSid')); + $this->assertEquals('10', $doc->getElement($path, $file)->getAttribute('w:cryptSpinCount')); + } + /** * Test compatibility */ @@ -66,4 +95,350 @@ class SettingsTest extends \PHPUnit_Framework_TestCase $this->assertTrue($doc->elementExists($path, $file)); $this->assertEquals($phpWord->getCompatibility()->getOoxmlVersion(), 15); } + + /** + * Test language + */ + public function testDefaultLanguage() + { + $phpWord = new PhpWord(); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:themeFontLang'; + $this->assertTrue($doc->elementExists($path, $file)); + $element = $doc->getElement($path, $file); + + $this->assertEquals('en-US', $element->getAttribute('w:val')); + } + + /** + * Test language + */ + public function testLanguage() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setThemeFontLang(new Language(Language::DE_DE, Language::KO_KR, Language::HE_IL)); + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:themeFontLang'; + $this->assertTrue($doc->elementExists($path, $file)); + $element = $doc->getElement($path, $file); + + $this->assertEquals(Language::DE_DE, $element->getAttribute('w:val')); + $this->assertEquals(Language::KO_KR, $element->getAttribute('w:eastAsia')); + $this->assertEquals(Language::HE_IL, $element->getAttribute('w:bidi')); + } + + /** + * Test proofState + */ + public function testProofState() + { + $proofState = new ProofState(); + $proofState->setSpelling(ProofState::DIRTY); + $proofState->setGrammar(ProofState::DIRTY); + $phpWord = new PhpWord(); + $phpWord->getSettings()->setProofState($proofState); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:proofState'; + $this->assertTrue($doc->elementExists($path, $file)); + $element = $doc->getElement($path, $file); + + $this->assertEquals('dirty', $element->getAttribute('w:spelling')); + $this->assertEquals('dirty', $element->getAttribute('w:grammar')); + } + + /** + * Test spelling + */ + public function testSpelling() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setHideSpellingErrors(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:hideSpellingErrors'; + $this->assertTrue($doc->elementExists($path, $file)); + $element = $doc->getElement($path, $file); + + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test even and odd headers + */ + public function testEvenAndOddHeaders() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setEvenAndOddHeaders(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:evenAndOddHeaders'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + public function testUpdateFields() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setUpdateFields(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:updateFields'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test zoom percentage + */ + public function testZoomPercentage() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setZoom(75); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:zoom'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertEquals('75', $element->getAttribute('w:percent')); + } + + /** + * Test zoom value + */ + public function testZoomValue() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setZoom(Zoom::FULL_PAGE); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:zoom'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertEquals('fullPage', $element->getAttribute('w:val')); + } + + public function testMirrorMargins() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setMirrorMargins(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:mirrorMargins'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test Revision View + */ + public function testRevisionView() + { + $trackChangesView = new TrackChangesView(); + $trackChangesView->setFormatting(false); + $trackChangesView->setComments(true); + + $phpWord = new PhpWord(); + $phpWord->getSettings()->setRevisionView($trackChangesView); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:revisionView'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertEquals('false', $element->getAttribute('w:formatting')); + $this->assertEquals('true', $element->getAttribute('w:comments')); + } + + public function testHideSpellingErrors() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setHideSpellingErrors(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:hideSpellingErrors'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + public function testHideGrammaticalErrors() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setHideGrammaticalErrors(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:hideGrammaticalErrors'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test track Revisions + */ + public function testTrackRevisions() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setTrackRevisions(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:trackRevisions'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test doNotTrackMoves + */ + public function testDoNotTrackMoves() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setDoNotTrackMoves(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:doNotTrackMoves'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + /** + * Test DoNotTrackFormatting + */ + public function testDoNotTrackFormatting() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setDoNotTrackFormatting(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:doNotTrackFormatting'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + public function testAutoHyphenation() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setAutoHyphenation(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:autoHyphenation'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } + + public function testConsecutiveHyphenLimit() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setConsecutiveHyphenLimit(2); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:consecutiveHyphenLimit'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('2', $element->getAttribute('w:val')); + } + + public function testHyphenationZone() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setHyphenationZone(100); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:hyphenationZone'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('100', $element->getAttribute('w:val')); + } + + public function testDoNotHyphenateCaps() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setDoNotHyphenateCaps(true); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:doNotHyphenateCaps'; + $this->assertTrue($doc->elementExists($path, $file)); + + $element = $doc->getElement($path, $file); + $this->assertSame('true', $element->getAttribute('w:val')); + } } diff --git a/tests/PhpWord/Writer/Word2007/Part/StylesTest.php b/tests/PhpWord/Writer/Word2007/Part/StylesTest.php index f40387a1..91f37184 100644 --- a/tests/PhpWord/Writer/Word2007/Part/StylesTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/StylesTest.php @@ -10,14 +10,17 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Part; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\Style\Font; +use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -26,7 +29,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Part\Styles * @runTestsInSeparateProcesses */ -class StylesTest extends \PHPUnit_Framework_TestCase +class StylesTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -74,4 +77,70 @@ class StylesTest extends \PHPUnit_Framework_TestCase $element = $doc->getElement($path, $file); $this->assertEquals('Normal', $element->getAttribute('w:val')); } + + public function testFontStyleBasedOn() + { + $phpWord = new PhpWord(); + + $baseParagraphStyle = new Paragraph(); + $baseParagraphStyle->setAlignment(Jc::CENTER); + $baseParagraphStyle = $phpWord->addParagraphStyle('BaseStyle', $baseParagraphStyle); + + $childFont = new Font(); + $childFont->setParagraph($baseParagraphStyle); + $childFont->setSize(16); + $childFont = $phpWord->addFontStyle('ChildFontStyle', $childFont); + + $otherFont = new Font(); + $otherFont->setSize(20); + $otherFont = $phpWord->addFontStyle('OtherFontStyle', $otherFont); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/styles.xml'; + + // Normal style generated? + $path = '/w:styles/w:style[@w:styleId="BaseStyle"]/w:name'; + $element = $doc->getElement($path, $file); + $this->assertEquals('BaseStyle', $element->getAttribute('w:val')); + + // Font style with paragraph should have it's base style set to that paragraphs style name + $path = '/w:styles/w:style[w:name/@w:val="ChildFontStyle"]/w:basedOn'; + $element = $doc->getElement($path, $file); + $this->assertEquals('BaseStyle', $element->getAttribute('w:val')); + + // Font style without paragraph should not have a base style set + $path = '/w:styles/w:style[w:name/@w:val="OtherFontStyle"]/w:basedOn'; + $element = $doc->getElement($path, $file); + $this->assertNull($element); + } + + public function testFontStyleBasedOnOtherFontStyle() + { + $phpWord = new PhpWord(); + + $styleGenerationP = new Paragraph(); + $styleGenerationP->setAlignment(Jc::BOTH); + + $styleGeneration = new Font(); + $styleGeneration->setParagraph($styleGenerationP); + $styleGeneration->setSize(9.5); + $phpWord->addFontStyle('Generation', $styleGeneration); + + $styleGenerationEteinteP = new Paragraph(); + $styleGenerationEteinteP->setBasedOn('Generation'); + + $styleGenerationEteinte = new Font(); + $styleGenerationEteinte->setParagraph($styleGenerationEteinteP); + $styleGenerationEteinte->setSize(8.5); + $phpWord->addFontStyle('GeneratEteinte', $styleGenerationEteinte); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/styles.xml'; + + $path = '/w:styles/w:style[@w:styleId="GeneratEteinte"]/w:basedOn'; + $element = $doc->getElement($path, $file); + $this->assertEquals('Generation', $element->getAttribute('w:val')); + } } diff --git a/tests/PhpWord/Writer/Word2007/PartTest.php b/tests/PhpWord/Writer/Word2007/PartTest.php index 7af8ce3a..277f61e1 100644 --- a/tests/PhpWord/Writer/Word2007/PartTest.php +++ b/tests/PhpWord/Writer/Word2007/PartTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007; use PhpOffice\PhpWord\Writer\Word2007\Part\RelsPart; @@ -23,7 +24,7 @@ use PhpOffice\PhpWord\Writer\Word2007\Part\RelsPart; * * Covers miscellaneous tests */ -class PartTest extends \PHPUnit_Framework_TestCase +class PartTest extends \PHPUnit\Framework\TestCase { /** * Test exception when no type or target assigned to a relation diff --git a/tests/PhpWord/Writer/Word2007/Style/FontTest.php b/tests/PhpWord/Writer/Word2007/Style/FontTest.php index 50a7ecf7..ccfffbfb 100644 --- a/tests/PhpWord/Writer/Word2007/Style/FontTest.php +++ b/tests/PhpWord/Writer/Word2007/Style/FontTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007\Style; use PhpOffice\PhpWord\TestHelperDOCX; @@ -24,7 +25,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Style\Font * @runTestsInSeparateProcesses */ -class FontTest extends \PHPUnit_Framework_TestCase +class FontTest extends \PHPUnit\Framework\TestCase { /** * Executed before each method of the class @@ -42,11 +43,41 @@ class FontTest extends \PHPUnit_Framework_TestCase $phpWord = new \PhpOffice\PhpWord\PhpWord(); $section = $phpWord->addSection(); $textrun = $section->addTextRun(); - $textrun->addText('سلام این یک پاراگراف راست به چپ است', array('rtl' => true)); + $textrun->addText('سلام این یک پاراگراف راست به چپ است', array('rtl' => true, 'lang' => 'ar-DZ')); $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); $file = 'word/document.xml'; $path = '/w:document/w:body/w:p/w:r/w:rPr/w:rtl'; $this->assertTrue($doc->elementExists($path, $file)); } + + /** + * Test writing font with language + */ + public function testFontWithLang() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('Ce texte-ci est en français.', array('lang' => \PhpOffice\PhpWord\Style\Language::FR_BE)); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $file = 'word/document.xml'; + $path = '/w:document/w:body/w:p/w:r/w:rPr/w:lang'; + $this->assertTrue($doc->elementExists($path, $file)); + } + + /** + * Test writing position + */ + public function testPosition() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('This text is lowered', array('position' => -20)); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:p/w:r/w:rPr/w:position'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals(-20, $doc->getElementAttribute($path, 'w:val')); + } } diff --git a/tests/PhpWord/Writer/Word2007/Style/ImageTest.php b/tests/PhpWord/Writer/Word2007/Style/ImageTest.php new file mode 100644 index 00000000..efa0a105 --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Style/ImageTest.php @@ -0,0 +1,69 @@ + Image::WRAP_INLINE, + 'wrapDistanceLeft' => 10, + 'wrapDistanceRight' => 20, + 'wrapDistanceTop' => 30, + 'wrapDistanceBottom' => 40, + ); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addImage(__DIR__ . '/../../../_files/images/earth.jpg', $styles); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:p[1]/w:r/w:pict/v:shape'; + $this->assertTrue($doc->elementExists($path . '/w10:wrap')); + $this->assertEquals('inline', $doc->getElementAttribute($path . '/w10:wrap', 'type')); + + $this->assertTrue($doc->elementExists($path)); + $style = $doc->getElement($path)->getAttribute('style'); + $this->assertNotNull($style); + $this->assertContains('mso-wrap-distance-left:10pt;', $style); + $this->assertContains('mso-wrap-distance-right:20pt;', $style); + $this->assertContains('mso-wrap-distance-top:30pt;', $style); + $this->assertContains('mso-wrap-distance-bottom:40pt;', $style); + } +} diff --git a/tests/PhpWord/Writer/Word2007/Style/ParagraphTest.php b/tests/PhpWord/Writer/Word2007/Style/ParagraphTest.php new file mode 100644 index 00000000..8443bbca --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Style/ParagraphTest.php @@ -0,0 +1,67 @@ +addParagraphStyle('testStyle', array('indent' => '10')); + $section = $phpWord->addSection(); + $section->addText('test', null, array('numStyle' => 'testStyle', 'numLevel' => '1')); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:p/w:pPr/w:numPr/w:ilvl'; + $this->assertTrue($doc->elementExists($path)); + } + + public function testSuppressAutoHyphens() + { + $paragraphStyle = new ParagraphStyle(); + $paragraphStyle->setSuppressAutoHyphens(true); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('test', null, $paragraphStyle); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:p/w:pPr/w:suppressAutoHyphens'; + $this->assertTrue($doc->elementExists($path)); + } +} diff --git a/tests/PhpWord/Writer/Word2007/Style/TableTest.php b/tests/PhpWord/Writer/Word2007/Style/TableTest.php new file mode 100644 index 00000000..ec3b2483 --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Style/TableTest.php @@ -0,0 +1,144 @@ +setLayout(Table::LAYOUT_FIXED); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable($tableStyle); + $table->addRow(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:tbl/w:tblPr/w:tblLayout'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals(Table::LAYOUT_FIXED, $doc->getElementAttribute($path, 'w:type')); + } + + /** + * Test write styles + */ + public function testCellSpacing() + { + $tableStyle = new Table(); + $tableStyle->setCellSpacing(10.3); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable($tableStyle); + $table->addRow(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:tbl/w:tblPr/w:tblCellSpacing'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals(10.3, $doc->getElementAttribute($path, 'w:w')); + $this->assertEquals(TblWidth::TWIP, $doc->getElementAttribute($path, 'w:type')); + } + + /** + * Test write table position + */ + public function testTablePosition() + { + $tablePosition = array( + 'leftFromText' => 10, + 'rightFromText' => 20, + 'topFromText' => 30, + 'bottomFromText' => 40, + 'vertAnchor' => TablePosition::VANCHOR_PAGE, + 'horzAnchor' => TablePosition::HANCHOR_MARGIN, + 'tblpXSpec' => TablePosition::XALIGN_CENTER, + 'tblpX' => 50, + 'tblpYSpec' => TablePosition::YALIGN_TOP, + 'tblpY' => 60, + ); + $tableStyle = new Table(); + $tableStyle->setPosition($tablePosition); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable($tableStyle); + $table->addRow(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:tbl/w:tblPr/w:tblpPr'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals(10, $doc->getElementAttribute($path, 'w:leftFromText')); + $this->assertEquals(20, $doc->getElementAttribute($path, 'w:rightFromText')); + $this->assertEquals(30, $doc->getElementAttribute($path, 'w:topFromText')); + $this->assertEquals(40, $doc->getElementAttribute($path, 'w:bottomFromText')); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $doc->getElementAttribute($path, 'w:vertAnchor')); + $this->assertEquals(TablePosition::HANCHOR_MARGIN, $doc->getElementAttribute($path, 'w:horzAnchor')); + $this->assertEquals(TablePosition::XALIGN_CENTER, $doc->getElementAttribute($path, 'w:tblpXSpec')); + $this->assertEquals(50, $doc->getElementAttribute($path, 'w:tblpX')); + $this->assertEquals(TablePosition::YALIGN_TOP, $doc->getElementAttribute($path, 'w:tblpYSpec')); + $this->assertEquals(60, $doc->getElementAttribute($path, 'w:tblpY')); + } + + public function testIndent() + { + $value = 100; + $type = TblWidth::TWIP; + + $tableStyle = new Table(); + $tableStyle->setIndent(new TblWidthComplexType($value, $type)); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable($tableStyle); + $table->addRow(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:tbl/w:tblPr/w:tblInd'; + $this->assertTrue($doc->elementExists($path)); + $this->assertSame($value, (int) $doc->getElementAttribute($path, 'w:w')); + $this->assertSame($type, $doc->getElementAttribute($path, 'w:type')); + } +} diff --git a/tests/PhpWord/Writer/Word2007/StyleTest.php b/tests/PhpWord/Writer/Word2007/StyleTest.php index dfabec03..48cff871 100644 --- a/tests/PhpWord/Writer/Word2007/StyleTest.php +++ b/tests/PhpWord/Writer/Word2007/StyleTest.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer\Word2007; use PhpOffice\Common\XMLWriter; @@ -21,7 +22,7 @@ use PhpOffice\Common\XMLWriter; /** * Test class for PhpOffice\PhpWord\Writer\Word2007\Style subnamespace */ -class StyleTest extends \PHPUnit_Framework_TestCase +class StyleTest extends \PHPUnit\Framework\TestCase { /** * Test empty styles diff --git a/tests/PhpWord/Writer/Word2007Test.php b/tests/PhpWord/Writer/Word2007Test.php index 76ba2114..0db36fc1 100644 --- a/tests/PhpWord/Writer/Word2007Test.php +++ b/tests/PhpWord/Writer/Word2007Test.php @@ -10,10 +10,11 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ + namespace PhpOffice\PhpWord\Writer; use PhpOffice\PhpWord\PhpWord; @@ -25,7 +26,7 @@ use PhpOffice\PhpWord\TestHelperDOCX; * * @runTestsInSeparateProcesses */ -class Word2007Test extends \PHPUnit_Framework_TestCase +class Word2007Test extends \PHPUnit\Framework\TestCase { /** * Tear down after each test @@ -74,7 +75,7 @@ class Word2007Test extends \PHPUnit_Framework_TestCase public function testSave() { $localImage = __DIR__ . '/../_files/images/earth.jpg'; - $remoteImage = 'http://php.net//images/logos/php-med-trans-light.gif'; + $remoteImage = 'http://php.net/images/logos/new-php-logo.png'; $phpWord = new PhpWord(); $phpWord->addFontStyle('Font', array('size' => 11)); $phpWord->addParagraphStyle('Paragraph', array('alignment' => Jc::CENTER)); @@ -96,7 +97,7 @@ class Word2007Test extends \PHPUnit_Framework_TestCase $file = __DIR__ . '/../_files/temp.docx'; $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } @@ -117,7 +118,7 @@ class Word2007Test extends \PHPUnit_Framework_TestCase $file = __DIR__ . '/../_files/temp.docx'; $writer->save($file); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); unlink($file); } @@ -166,6 +167,8 @@ class Word2007Test extends \PHPUnit_Framework_TestCase */ public function testSetGetUseDiskCaching() { + $this->setOutputCallback(function () { + }); $phpWord = new PhpWord(); $phpWord->addSection(); $object = new Word2007($phpWord); @@ -183,7 +186,7 @@ class Word2007Test extends \PHPUnit_Framework_TestCase */ public function testSetUseDiskCachingException() { - $dir = join(DIRECTORY_SEPARATOR, array(PHPWORD_TESTS_BASE_DIR, 'foo')); + $dir = implode(DIRECTORY_SEPARATOR, array(PHPWORD_TESTS_BASE_DIR, 'foo')); $object = new Word2007(); $object->setUseDiskCaching(true, $dir); diff --git a/tests/PhpWord/_files/documents/reader-2011.docx b/tests/PhpWord/_files/documents/reader-2011.docx new file mode 100644 index 00000000..be94eca5 Binary files /dev/null and b/tests/PhpWord/_files/documents/reader-2011.docx differ diff --git a/tests/PhpWord/_files/documents/reader.doc b/tests/PhpWord/_files/documents/reader.doc new file mode 100644 index 00000000..a5ce295d Binary files /dev/null and b/tests/PhpWord/_files/documents/reader.doc differ diff --git a/tests/PhpWord/_files/documents/reader.docx b/tests/PhpWord/_files/documents/reader.docx index d09091b1..65c761e0 100644 Binary files a/tests/PhpWord/_files/documents/reader.docx and b/tests/PhpWord/_files/documents/reader.docx differ diff --git a/tests/PhpWord/_files/templates/document22-xml.docx b/tests/PhpWord/_files/templates/document22-xml.docx new file mode 100644 index 00000000..206d80f4 Binary files /dev/null and b/tests/PhpWord/_files/templates/document22-xml.docx differ diff --git a/tests/PhpWord/_includes/AbstractTestReader.php b/tests/PhpWord/_includes/AbstractTestReader.php new file mode 100644 index 00000000..d9097d71 --- /dev/null +++ b/tests/PhpWord/_includes/AbstractTestReader.php @@ -0,0 +1,64 @@ + array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Styles', 'xml' => '{toReplace}'), + 'document' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Document', 'xml' => '{toReplace}'), + 'footnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Footnotes', 'xml' => '{toReplace}'), + 'endnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Endnotes', 'xml' => '{toReplace}'), + 'settings' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Settings', 'xml' => '{toReplace}'), + ); + + /** + * Builds a PhpWord instance based on the xml passed + * + * @param string $documentXml + * @param null|string $stylesXml + * @return \PhpOffice\PhpWord\PhpWord + */ + protected function getDocumentFromString(array $partXmls = array()) + { + $file = __DIR__ . '/../_files/temp.docx'; + $zip = new \ZipArchive(); + $zip->open($file, \ZipArchive::CREATE); + foreach ($this->parts as $partName => $part) { + if (array_key_exists($partName, $partXmls)) { + $zip->addFromString("{$partName}.xml", str_replace('{toReplace}', $partXmls[$partName], $this->parts[$partName]['xml'])); + } + } + $zip->close(); + + $phpWord = new PhpWord(); + foreach ($this->parts as $partName => $part) { + if (array_key_exists($partName, $partXmls)) { + $className = $this->parts[$partName]['class']; + $reader = new $className($file, "{$partName}.xml"); + $reader->read($phpWord); + } + } + unlink($file); + + return $phpWord; + } +} diff --git a/tests/PhpWord/_includes/TestHelperDOCX.php b/tests/PhpWord/_includes/TestHelperDOCX.php index 03079974..02fa7d78 100644 --- a/tests/PhpWord/_includes/TestHelperDOCX.php +++ b/tests/PhpWord/_includes/TestHelperDOCX.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -29,7 +29,7 @@ class TestHelperDOCX * * @var string */ - static protected $file; + protected static $file; /** * Get document content @@ -39,9 +39,8 @@ class TestHelperDOCX * @param \PhpOffice\PhpWord\PhpWord $phpWord * @param string $writerName * - * @return \PhpOffice\PhpWord\XmlDocument - * * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException + * @return \PhpOffice\PhpWord\XmlDocument */ public static function getDocument(PhpWord $phpWord, $writerName = 'Word2007') { @@ -57,7 +56,7 @@ class TestHelperDOCX $xmlWriter = IOFactory::createWriter($phpWord, $writerName); $xmlWriter->save(self::$file); - $zip = new \ZipArchive; + $zip = new \ZipArchive(); $res = $zip->open(self::$file); if (true === $res) { $zip->extractTo(Settings::getTempDir() . '/PhpWord_Unit_Test/'); diff --git a/tests/PhpWord/_includes/XmlDocument.php b/tests/PhpWord/_includes/XmlDocument.php index 72b18c29..7062ebbf 100644 --- a/tests/PhpWord/_includes/XmlDocument.php +++ b/tests/PhpWord/_includes/XmlDocument.php @@ -10,8 +10,8 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ @@ -25,7 +25,7 @@ class XmlDocument /** * Path * - * @var string $path + * @var string */ private $path; @@ -37,9 +37,9 @@ class XmlDocument private $dom; /** - * DOMXpath object + * DOMXPath object * - * @var \DOMXpath + * @var \DOMXPath */ private $xpath; @@ -76,8 +76,11 @@ class XmlDocument $this->file = $file; $file = $this->path . '/' . $file; + libxml_disable_entity_loader(false); $this->dom = new \DOMDocument(); $this->dom->load($file); + libxml_disable_entity_loader(true); + return $this->dom; } @@ -95,8 +98,8 @@ class XmlDocument } if (null === $this->xpath) { - $this->xpath = new \DOMXpath($this->dom); - + $this->xpath = new \DOMXPath($this->dom); + $this->xpath->registerNamespace('w14', 'http://schemas.microsoft.com/office/word/2010/wordml'); } return $this->xpath->query($path); @@ -159,6 +162,33 @@ class XmlDocument public function elementExists($path, $file = 'word/document.xml') { $nodeList = $this->getNodeList($path, $file); + return !($nodeList->length == 0); } + + /** + * Returns the xml, or part of it as a formatted string + * + * @param string $path + * @param string $file + * @return string + */ + public function printXml($path = '/', $file = 'word/document.xml') + { + $element = $this->getElement($path, $file); + if ($element instanceof \DOMDocument) { + $element->formatOutput = true; + $element->preserveWhiteSpace = false; + + return $element->saveXML(); + } + + $newdoc = new \DOMDocument(); + $newdoc->formatOutput = true; + $newdoc->preserveWhiteSpace = false; + $node = $newdoc->importNode($element, true); + $newdoc->appendChild($node); + + return $newdoc->saveXML($node); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 60ca5ae7..c1681bcd 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,11 +10,10 @@ * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. test bootstrap * - * @link https://github.com/PHPOffice/PHPWord - * @copyright 2010-2016 PHPWord contributors + * @see https://github.com/PHPOffice/PHPWord + * @copyright 2010-2018 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ - require_once __DIR__ . '/../bootstrap.php'; date_default_timezone_set('UTC'); @@ -29,7 +28,7 @@ spl_autoload_register(function ($class) { $prefix = 'PhpOffice\\PhpWord'; if (strpos($class, $prefix) === 0) { $class = str_replace('\\', DIRECTORY_SEPARATOR, $class); - $class = join(DIRECTORY_SEPARATOR, array('PhpWord', '_includes')) . + $class = implode(DIRECTORY_SEPARATOR, array('PhpWord', '_includes')) . substr($class, strlen($prefix)); $file = __DIR__ . DIRECTORY_SEPARATOR . $class . '.php'; if (file_exists($file)) {