Merge branch 'develop' into html_writer_auto_invert_text_color
This commit is contained in:
commit
8931ab12b2
|
|
@ -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
|
||||||
|
|
@ -13,11 +13,11 @@ composer.phar
|
||||||
vendor
|
vendor
|
||||||
/report
|
/report
|
||||||
/build
|
/build
|
||||||
/samples/resources
|
|
||||||
/samples/results
|
/samples/results
|
||||||
/.settings
|
/.settings
|
||||||
phpword.ini
|
phpword.ini
|
||||||
/.buildpath
|
/.buildpath
|
||||||
|
/.scannerwork
|
||||||
/.project
|
/.project
|
||||||
/nbproject
|
/nbproject
|
||||||
/.php_cs.cache
|
/.php_cs.cache
|
||||||
|
|
|
||||||
19
.travis.yml
19
.travis.yml
|
|
@ -10,11 +10,20 @@ php:
|
||||||
- 7.0
|
- 7.0
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
|
- 7.3
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- php: 5.6
|
- php: 7.0
|
||||||
env: COVERAGE=1
|
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
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
|
|
@ -32,12 +41,12 @@ before_install:
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
## Deactivate xdebug if we don't do code coverage
|
## Deactivate xdebug if we don't do code coverage
|
||||||
- if [ -z "$COVERAGE" ]; then phpenv config-rm xdebug.ini ; fi
|
- if [ -z "$COVERAGE" ]; then phpenv config-rm xdebug.ini || echo "xdebug not available" ; fi
|
||||||
## Composer
|
## Composer
|
||||||
- composer self-update
|
- composer self-update
|
||||||
- travis_wait composer install --prefer-source
|
- travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi)
|
||||||
## PHPDocumentor
|
## PHPDocumentor
|
||||||
- mkdir -p build/docs
|
##- mkdir -p build/docs
|
||||||
- mkdir -p build/coverage
|
- mkdir -p build/coverage
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
|
@ -52,7 +61,7 @@ script:
|
||||||
## PHPLOC
|
## PHPLOC
|
||||||
- if [ -z "$COVERAGE" ]; then ./vendor/bin/phploc src/ ; fi
|
- if [ -z "$COVERAGE" ]; then ./vendor/bin/phploc src/ ; fi
|
||||||
## PHPDocumentor
|
## PHPDocumentor
|
||||||
- if [ -z "$COVERAGE" ]; then ./vendor/bin/phpdoc -q -d ./src -t ./build/docs --ignore "*/src/PhpWord/Shared/*/*" --template="responsive-twig" ; fi
|
##- if [ -z "$COVERAGE" ]; then ./vendor/bin/phpdoc -q -d ./src -t ./build/docs --ignore "*/src/PhpWord/Shared/*/*" --template="responsive-twig" ; fi
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
## Coveralls
|
## Coveralls
|
||||||
|
|
|
||||||
48
CHANGELOG.md
48
CHANGELOG.md
|
|
@ -3,7 +3,47 @@ Change Log
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
v0.15.0 (?? ??? 2018)
|
v0.17.0 (?? ??? 2019)
|
||||||
|
----------------------
|
||||||
|
### Added
|
||||||
|
- Add RightToLeft table presentation. @troosan #1550
|
||||||
|
- Set complex type in template @troosan #1565
|
||||||
|
- Add support for page vertical alignment. @troosan #672 #1569
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix HTML border-color parsing. @troosan #1551 #1570
|
||||||
|
|
||||||
|
### Miscelaneous
|
||||||
|
- Use embedded http server to test loading of remote images @troosan #
|
||||||
|
|
||||||
|
v0.16.0 (30 dec 2018)
|
||||||
|
----------------------
|
||||||
|
### Added
|
||||||
|
- Add getVariableCount method in TemplateProcessor. @nicoder #1272
|
||||||
|
- Add setting Chart Title and Legend visibility @Tom-Magill #1433
|
||||||
|
- Add ability to pass a Style object in Section constructor @ndench #1416
|
||||||
|
- Add support for hidden text @Alexmg86 #1527
|
||||||
|
- Add support for setting images in TemplateProcessor @SailorMax #1170
|
||||||
|
- Add "Plain Text" type to SDT (Structured Document Tags) @morrisdj #1541
|
||||||
|
- Added possibility to index variables inside cloned block in TemplateProcessor @JPBetley #817
|
||||||
|
- Added possibility to replace variables inside cloned block with values in TemplateProcessor @DIDoS #1392
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix regex in `cloneBlock` function @nicoder #1269
|
||||||
|
- HTML Title Writer loses text when Title contains a TextRun instead a string. @begnini #1436
|
||||||
|
- Fix regex in fixBrokenMacros, make it less greedy @MuriloSo @brainwood @yurii-sio2 #1502 #1345
|
||||||
|
- 240 twips are being added to line spacing, should not happen when using lineRule fixed @troosan #1509 #1505
|
||||||
|
- 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
|
||||||
|
- For RTF writers, sizes should should never have decimals @Samuel-BF #1536
|
||||||
|
- Style Name Parsing fails if document generated by a non-english word version @begnini #1434
|
||||||
|
|
||||||
|
### Miscelaneous
|
||||||
|
- Get rid of duplicated code in TemplateProcessor @abcdmitry #1161
|
||||||
|
|
||||||
|
v0.15.0 (14 Jul 2018)
|
||||||
----------------------
|
----------------------
|
||||||
### Added
|
### Added
|
||||||
- Parsing of `align` HTML attribute - @troosan #1231
|
- Parsing of `align` HTML attribute - @troosan #1231
|
||||||
|
|
@ -23,6 +63,9 @@ v0.15.0 (?? ??? 2018)
|
||||||
- Add support for table indent (tblInd) @Trainmaster #1343
|
- Add support for table indent (tblInd) @Trainmaster #1343
|
||||||
- Added parsing of internal links in HTML reader @lalop #1336
|
- Added parsing of internal links in HTML reader @lalop #1336
|
||||||
- Several improvements to charts @JAEK-S #1332
|
- 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
|
### Fixed
|
||||||
- Fix reading of docx default style - @troosan #1238
|
- Fix reading of docx default style - @troosan #1238
|
||||||
|
|
@ -35,6 +78,9 @@ v0.15.0 (?? ??? 2018)
|
||||||
- Fix colspan and rowspan for tables in HTML Writer @mattbolt #1292
|
- Fix colspan and rowspan for tables in HTML Writer @mattbolt #1292
|
||||||
- Fix parsing of Heading and Title formating @troosan @gthomas2 #465
|
- Fix parsing of Heading and Title formating @troosan @gthomas2 #465
|
||||||
- Fix Dateformat typo, fix hours casing, add Month-Day-Year formats @ComputerTinker #591
|
- 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
|
### Changed
|
||||||
- Remove zend-stdlib dependency @Trainmaster #1284
|
- Remove zend-stdlib dependency @Trainmaster #1284
|
||||||
|
|
|
||||||
|
|
@ -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.
|
- **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.
|
- **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 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.
|
- **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.readthedocs.io) you can find documentation on how to write tests with PHPUnit, 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.
|
- **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!
|
That's it. Thank you for your interest in PHPWord, and welcome!
|
||||||
|
|
|
||||||
15
README.md
15
README.md
|
|
@ -1,13 +1,20 @@
|
||||||
# 
|
# 
|
||||||
|
|
||||||
|
Master:
|
||||||
[](https://packagist.org/packages/phpoffice/phpword)
|
[](https://packagist.org/packages/phpoffice/phpword)
|
||||||
[](https://travis-ci.org/PHPOffice/PHPWord)
|
[](https://travis-ci.org/PHPOffice/PHPWord)
|
||||||
[](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/)
|
[](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/)
|
||||||
[](https://coveralls.io/github/PHPOffice/PHPWord?branch=develop)
|
[](https://coveralls.io/github/PHPOffice/PHPWord?branch=master)
|
||||||
[](https://packagist.org/packages/phpoffice/phpword)
|
[](https://packagist.org/packages/phpoffice/phpword)
|
||||||
[](https://packagist.org/packages/phpoffice/phpword)
|
[](https://packagist.org/packages/phpoffice/phpword)
|
||||||
[](https://gitter.im/PHPOffice/PHPWord)
|
[](https://gitter.im/PHPOffice/PHPWord)
|
||||||
|
|
||||||
|
Develop:
|
||||||
|
[](https://packagist.org/packages/phpoffice/phpword#dev-develop)
|
||||||
|
[](https://travis-ci.org/PHPOffice/PHPWord/branches)
|
||||||
|
[](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/?branch=develop)
|
||||||
|
[](https://coveralls.io/github/PHPOffice/PHPWord?branch=develop)
|
||||||
|
|
||||||
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 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/).
|
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/).
|
||||||
|
|
@ -81,7 +88,7 @@ You can of course also manually edit your composer.json file
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"phpoffice/phpword": "v0.14.*"
|
"phpoffice/phpword": "v0.16.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -161,7 +168,7 @@ $objWriter->save('helloWorld.html');
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
You can also read the [Developers' Documentation](http://phpword.readthedocs.org/) for more detail.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
"php-cs-fixer fix --ansi --dry-run --diff",
|
"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",
|
"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",
|
"phpmd src/,tests/ text ./phpmd.xml.dist --exclude pclzip.lib.php",
|
||||||
"@test"
|
"@test-no-coverage"
|
||||||
],
|
],
|
||||||
"fix": [
|
"fix": [
|
||||||
"php-cs-fixer fix --ansi"
|
"php-cs-fixer fix --ansi"
|
||||||
|
|
@ -61,18 +61,19 @@
|
||||||
"php": "^5.3.3 || ^7.0",
|
"php": "^5.3.3 || ^7.0",
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"zendframework/zend-escaper": "^2.2",
|
"zendframework/zend-escaper": "^2.2",
|
||||||
"phpoffice/common": "^0.2"
|
"phpoffice/common": "^0.2.9"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^4.8.36 || ^5.0",
|
"ext-zip": "*",
|
||||||
"phpdocumentor/phpdocumentor":"2.*",
|
"ext-gd": "*",
|
||||||
"squizlabs/php_codesniffer": "^2.7",
|
"phpunit/phpunit": "^4.8.36 || ^7.0",
|
||||||
"friendsofphp/php-cs-fixer": "^2.0",
|
"squizlabs/php_codesniffer": "^2.9",
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.2",
|
||||||
"phpmd/phpmd": "2.*",
|
"phpmd/phpmd": "2.*",
|
||||||
"phploc/phploc": "2.* || 3.* || 4.*",
|
"phploc/phploc": "2.* || 3.* || 4.*",
|
||||||
"dompdf/dompdf":"0.8.*",
|
"dompdf/dompdf":"0.8.*",
|
||||||
"tecnickcom/tcpdf": "6.*",
|
"tecnickcom/tcpdf": "6.*",
|
||||||
"mpdf/mpdf": "5.* || 6.* || 7.*",
|
"mpdf/mpdf": "5.7.4 || 6.* || 7.*",
|
||||||
"php-coveralls/php-coveralls": "1.1.0 || ^2.0"
|
"php-coveralls/php-coveralls": "1.1.0 || ^2.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
|
@ -89,7 +90,7 @@
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-develop": "0.15-dev"
|
"dev-develop": "0.17-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@ Fixes # (issue)
|
||||||
|
|
||||||
- [ ] I have run `composer run-script check --timeout=0` and no errors were reported
|
- [ ] 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)
|
- [ ] The new code is covered by unit tests (check build/coverage for coverage report)
|
||||||
- [ ] I have update the documentation to describe the changes
|
- [ ] I have updated the documentation to describe the changes
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ copyright = u'2014-2017, PHPWord Contributors'
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.14.0'
|
version = '0.16.0'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,12 @@ You can pass an optional parameter to specify where the header/footer should be
|
||||||
- ``Footer::FIRST`` each first page of the section
|
- ``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
|
- ``Footer::EVEN`` each even page of the section. Will only be applied if the evenAndOddHeaders is set to true in phpWord->settings
|
||||||
|
|
||||||
|
To change the evenAndOddHeaders use the ``getSettings`` method to return the Settings object, and then call the ``setEvenAndOddHeaders`` method:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$phpWord->getSettings()->setEvenAndOddHeaders(true);
|
||||||
|
|
||||||
Footers
|
Footers
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ Legend:
|
||||||
Texts
|
Texts
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Text can be added by using ``addText`` and ``addTextRun`` method.
|
Text can be added by using ``addText`` and ``addTextRun`` methods.
|
||||||
``addText`` is used for creating simple paragraphs that only contain texts with the same style.
|
``addText`` is used for creating simple paragraphs that only contain texts with the same style.
|
||||||
``addTextRun`` is used for creating complex paragraphs that contain text with different style (some bold, other
|
``addTextRun`` is used for creating complex paragraphs that contain text with different style (some bold, other
|
||||||
italics, etc) or other elements, e.g. images or links. The syntaxes are as follow:
|
italics, etc) or other elements, e.g. images or links. The syntaxes are as follow:
|
||||||
|
|
@ -155,13 +155,18 @@ method or using the ``pageBreakBefore`` style of paragraph.
|
||||||
Lists
|
Lists
|
||||||
-----
|
-----
|
||||||
|
|
||||||
To add a list item use the function ``addListItem``.
|
Lists can be added by using ``addListItem`` and ``addListItemRun`` methods.
|
||||||
|
``addListItem`` is used for creating lists that only contain plain text.
|
||||||
|
``addListItemRun`` is used for creating complex list items that contains texts
|
||||||
|
with different style (some bold, other italics, etc) or other elements, e.g.
|
||||||
|
images or links. The syntaxes are as follow:
|
||||||
|
|
||||||
Basic usage:
|
Basic usage:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$section->addListItem($text, [$depth], [$fontStyle], [$listStyle], [$paragraphStyle]);
|
$section->addListItem($text, [$depth], [$fontStyle], [$listStyle], [$paragraphStyle]);
|
||||||
|
$listItemRun = $section->addListItemRun([$depth], [$listStyle], [$paragraphStyle])
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
|
|
@ -172,6 +177,8 @@ Parameters:
|
||||||
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`.
|
- ``$paragraphStyle``. See :ref:`paragraph-style`.
|
||||||
|
|
||||||
|
See ``Sample_09_Tables.php`` for more code sample.
|
||||||
|
|
||||||
Advanced usage:
|
Advanced usage:
|
||||||
|
|
||||||
You can also create your own numbering style by changing the ``$listStyle`` parameter with the name of your numbering style.
|
You can also create your own numbering style by changing the ``$listStyle`` parameter with the name of your numbering style.
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,11 @@ Available Section style options:
|
||||||
- ``marginRight``. Page margin right in *twip*.
|
- ``marginRight``. Page margin right in *twip*.
|
||||||
- ``marginBottom``. Page margin bottom in *twip*.
|
- ``marginBottom``. Page margin bottom in *twip*.
|
||||||
- ``orientation``. Page orientation (``portrait``, which is default, or ``landscape``).
|
- ``orientation``. Page orientation (``portrait``, which is default, or ``landscape``).
|
||||||
|
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.
|
- ``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.
|
- ``pageSizeW``. Page width in *twip*. Implicitly defined by ``orientation`` option. Any changes are discouraged.
|
||||||
|
- ``vAlign``. Vertical Page Alignment
|
||||||
|
See ``\PhpOffice\PhpWord\SimpleType\VerticalJc`` for possible values
|
||||||
|
|
||||||
.. _font-style:
|
.. _font-style:
|
||||||
|
|
||||||
|
|
@ -45,7 +48,7 @@ Available Font style options:
|
||||||
- ``color``. Font color, e.g. *FF0000*.
|
- ``color``. Font color, e.g. *FF0000*.
|
||||||
- ``doubleStrikethrough``. Double strikethrough, *true* or *false*.
|
- ``doubleStrikethrough``. Double strikethrough, *true* or *false*.
|
||||||
- ``fgColor``. Font highlight color, e.g. *yellow*, *green*, *blue*.
|
- ``fgColor``. Font highlight color, e.g. *yellow*, *green*, *blue*.
|
||||||
See ``\PhpOffice\PhpWord\Style\Font::FGCOLOR_...`` constants for more values
|
See ``\PhpOffice\PhpWord\Style\Font::FGCOLOR_...`` class constants for possible values
|
||||||
- ``hint``. Font content type, *default*, *eastAsia*, or *cs*.
|
- ``hint``. Font content type, *default*, *eastAsia*, or *cs*.
|
||||||
- ``italic``. Italic, *true* or *false*.
|
- ``italic``. Italic, *true* or *false*.
|
||||||
- ``name``. Font name, e.g. *Arial*.
|
- ``name``. Font name, e.g. *Arial*.
|
||||||
|
|
@ -56,10 +59,11 @@ Available Font style options:
|
||||||
- ``subScript``. Subscript, *true* or *false*.
|
- ``subScript``. Subscript, *true* or *false*.
|
||||||
- ``superScript``. Superscript, *true* or *false*.
|
- ``superScript``. Superscript, *true* or *false*.
|
||||||
- ``underline``. Underline, *single*, *dash*, *dotted*, etc.
|
- ``underline``. Underline, *single*, *dash*, *dotted*, etc.
|
||||||
See ``\PhpOffice\PhpWord\Style\Font::UNDERLINE_...`` constants for more values
|
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
|
- ``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.
|
See ``\PhpOffice\PhpWord\Style\Language`` class for some language codes.
|
||||||
- ``position``. The text position, raised or lowered, in half points
|
- ``position``. The text position, raised or lowered, in half points
|
||||||
|
- ``hidden``. Hidden text, *true* or *false*.
|
||||||
|
|
||||||
.. _paragraph-style:
|
.. _paragraph-style:
|
||||||
|
|
||||||
|
|
@ -69,7 +73,7 @@ Paragraph
|
||||||
Available Paragraph style options:
|
Available Paragraph style options:
|
||||||
|
|
||||||
- ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012.
|
- ``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.
|
- ``basedOn``. Parent style.
|
||||||
- ``hanging``. Hanging in *twip*.
|
- ``hanging``. Hanging in *twip*.
|
||||||
- ``indent``. Indent in *twip*.
|
- ``indent``. Indent in *twip*.
|
||||||
|
|
@ -80,8 +84,9 @@ Available Paragraph style options:
|
||||||
- ``pageBreakBefore``. Start paragraph on next page, *true* or *false*.
|
- ``pageBreakBefore``. Start paragraph on next page, *true* or *false*.
|
||||||
- ``spaceBefore``. Space before paragraph in *twip*.
|
- ``spaceBefore``. Space before paragraph in *twip*.
|
||||||
- ``spaceAfter``. Space after paragraph in *twip*.
|
- ``spaceAfter``. Space after paragraph in *twip*.
|
||||||
- ``spacing``. Space between lines.
|
- ``spacing``. Space between lines in *twip*. If spacingLineRule is auto, 240 (height of 1 line) will be added, so if you want a double line height, set this to 240.
|
||||||
- ``spacingLineRule``. Line Spacing Rule. *auto*, *exact*, *atLeast*
|
- ``spacingLineRule``. Line Spacing Rule. *auto*, *exact*, *atLeast*
|
||||||
|
See ``\PhpOffice\PhpWord\SimpleType\LineSpacingRule`` class constants for possible values.
|
||||||
- ``suppressAutoHyphens``. Hyphenation for paragraph, *true* or *false*.
|
- ``suppressAutoHyphens``. Hyphenation for paragraph, *true* or *false*.
|
||||||
- ``tabs``. Set of custom tab stops.
|
- ``tabs``. Set of custom tab stops.
|
||||||
- ``widowControl``. Allow first/last line to display on a separate page, *true* or *false*.
|
- ``widowControl``. Allow first/last line to display on a separate page, *true* or *false*.
|
||||||
|
|
@ -89,7 +94,7 @@ Available Paragraph style options:
|
||||||
- ``bidi``. Right to Left Paragraph Layout, *true* or *false*.
|
- ``bidi``. Right to Left Paragraph Layout, *true* or *false*.
|
||||||
- ``shading``. Paragraph Shading.
|
- ``shading``. Paragraph Shading.
|
||||||
- ``textAlignment``. Vertical Character Alignment on Line.
|
- ``textAlignment``. Vertical Character Alignment on Line.
|
||||||
See ``\PhpOffice\PhpWord\SimpleType\TextAlignment`` class for possible values.
|
See ``\PhpOffice\PhpWord\SimpleType\TextAlignment`` class constants for possible values.
|
||||||
|
|
||||||
.. _table-style:
|
.. _table-style:
|
||||||
|
|
||||||
|
|
@ -99,17 +104,18 @@ Table
|
||||||
Available Table style options:
|
Available Table style options:
|
||||||
|
|
||||||
- ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012.
|
- ``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'.
|
- ``bgColor``. Background color, e.g. '9966CC'.
|
||||||
- ``border(Top|Right|Bottom|Left)Color``. Border color, e.g. '9966CC'.
|
- ``border(Top|Right|Bottom|Left)Color``. Border color, e.g. '9966CC'.
|
||||||
- ``border(Top|Right|Bottom|Left)Size``. Border size in *twip*.
|
- ``border(Top|Right|Bottom|Left)Size``. Border size in *twip*.
|
||||||
- ``cellMargin(Top|Right|Bottom|Left)``. Cell margin 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``.
|
- ``indent``. Table indent from leading margin. Must be an instance of ``\PhpOffice\PhpWord\ComplexType\TblWidth``.
|
||||||
- ``width``. Table width in percent.
|
- ``width``. Table width in Fiftieths of a Percent or Twentieths of a Point.
|
||||||
- ``unit``. The unit to use for the width. One of ``\PhpOffice\PhpWord\SimpleType\TblWidth``. Defaults to *auto*.
|
- ``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.
|
- ``layout``. Table layout, either *fixed* or *autofit* See ``\PhpOffice\PhpWord\Style\Table`` for constants.
|
||||||
- ``cellSpacing`` Cell spacing in *twip*
|
- ``cellSpacing`` Cell spacing in *twip*
|
||||||
- ``position`` Floating Table Positioning, see below for options
|
- ``position`` Floating Table Positioning, see below for options
|
||||||
|
- ``bidiVisual`` Present table as Right-To-Left
|
||||||
|
|
||||||
Floating Table Positioning options:
|
Floating Table Positioning options:
|
||||||
|
|
||||||
|
|
@ -168,7 +174,7 @@ Numbering level
|
||||||
Available NumberingLevel style options:
|
Available NumberingLevel style options:
|
||||||
|
|
||||||
- ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012.
|
- ``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.
|
- ``font``. Font name.
|
||||||
- ``format``. Numbering format bullet\|decimal\|upperRoman\|lowerRoman\|upperLetter\|lowerLetter.
|
- ``format``. Numbering format bullet\|decimal\|upperRoman\|lowerRoman\|upperLetter\|lowerLetter.
|
||||||
- ``hanging``. See paragraph style.
|
- ``hanging``. See paragraph style.
|
||||||
|
|
@ -190,6 +196,14 @@ Available Chart style options:
|
||||||
- ``width``. Width (in EMU).
|
- ``width``. Width (in EMU).
|
||||||
- ``height``. Height (in EMU).
|
- ``height``. Height (in EMU).
|
||||||
- ``3d``. Is 3D; applies to pie, bar, line, area, *true* or *false*.
|
- ``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*.
|
- ``showAxisLabels``. Show labels for axis, *true* or *false*.
|
||||||
- ``gridX``. Show Gridlines for X-Axis, *true* or *false*.
|
- ``gridX``. Show Gridlines for X-Axis, *true* or *false*.
|
||||||
- ``gridY``. Show Gridlines for Y-Axis, *true* or *false*.
|
- ``gridY``. Show Gridlines for Y-Axis, *true* or *false*.
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,243 @@ Templates processing
|
||||||
====================
|
====================
|
||||||
|
|
||||||
You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced.
|
You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced.
|
||||||
|
Macros are defined like this: ``${search-pattern}``.
|
||||||
|
To load a template file, create a new instance of the TemplateProcessor.
|
||||||
|
|
||||||
To deal with a template file, use ``new TemplateProcessor`` statement. After TemplateProcessor instance creation the document template is copied into the temporary directory. Then you can use ``TemplateProcessor::setValue`` method to change the value of a search pattern. The search-pattern model is: ``${search-pattern}``.
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor = new TemplateProcessor('Template.docx');
|
||||||
|
|
||||||
|
setValue
|
||||||
|
""""""""
|
||||||
|
Given a template containing
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
Hello ${firstname} ${lastname}!
|
||||||
|
|
||||||
|
The following will replace ``${firstname}`` with ``John``, and ``${lastname}`` with ``Doe`` .
|
||||||
|
The resulting document will now contain ``Hello John Doe!``
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->setValue('firstname', 'John');
|
||||||
|
$templateProcessor->setValue('lastname', 'Doe');
|
||||||
|
|
||||||
|
setValues
|
||||||
|
"""""""""
|
||||||
|
You can also set multiple values by passing all of them in an array.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe'));
|
||||||
|
|
||||||
|
setImageValue
|
||||||
|
"""""""""""""
|
||||||
|
The search-pattern model for images can be like:
|
||||||
|
- ``${search-image-pattern}``
|
||||||
|
- ``${search-image-pattern:[width]:[height]:[ratio]}``
|
||||||
|
- ``${search-image-pattern:[width]x[height]}``
|
||||||
|
- ``${search-image-pattern:size=[width]x[height]}``
|
||||||
|
- ``${search-image-pattern:width=[width]:height=[height]:ratio=false}``
|
||||||
|
|
||||||
|
Where:
|
||||||
|
- [width] and [height] can be just numbers or numbers with measure, which supported by Word (cm, mm, in, pt, pc, px, %, em, ex)
|
||||||
|
- [ratio] uses only for ``false``, ``-`` or ``f`` to turn off respect aspect ration of image. By default template image size uses as 'container' size.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
${CompanyLogo}
|
||||||
|
${UserLogo:50:50} ${Name} - ${City} - ${Street}
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$templateProcessor = new TemplateProcessor('Template.docx');
|
$templateProcessor = new TemplateProcessor('Template.docx');
|
||||||
$templateProcessor->setValue('Name', 'John Doe');
|
$templateProcessor->setValue('Name', 'John Doe');
|
||||||
$templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street'));
|
$templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street'));
|
||||||
|
|
||||||
It is not possible to directly add new OOXML elements to the template file being processed, but it is possible to transform headers, main document part, and footers of the template using XSLT (see ``TemplateProcessor::applyXslStyleSheet``).
|
$templateProcessor->setImageValue('CompanyLogo', 'path/to/company/logo.png');
|
||||||
|
$templateProcessor->setImageValue('UserLogo', array('path' => 'path/to/logo.png', 'width' => 100, 'height' => 100, 'ratio' => false));
|
||||||
|
|
||||||
See ``Sample_07_TemplateCloneRow.php`` for example on how to create
|
cloneBlock
|
||||||
multirow from a single row in a template by using ``TemplateProcessor::cloneRow``.
|
""""""""""
|
||||||
|
Given a template containing
|
||||||
|
See ``Sample_23_TemplateBlock.php`` for an example.
|
||||||
|
|
||||||
See ``Sample_23_TemplateBlock.php`` for example on how to clone a block
|
.. code-block:: clean
|
||||||
of text using ``TemplateProcessor::cloneBlock`` and delete a block of text using
|
|
||||||
``TemplateProcessor::deleteBlock``.
|
${block_name}
|
||||||
|
Customer: ${customer_name}
|
||||||
|
Address: ${customer_address}
|
||||||
|
${/block_name}
|
||||||
|
|
||||||
|
The following will duplicate everything between ``${block_name}`` and ``${/block_name}`` 3 times.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->cloneBlock('block_name', 3, true, true);
|
||||||
|
|
||||||
|
The last parameter will rename any macro defined inside the block and add #1, #2, #3 ... to the macro name.
|
||||||
|
The result will be
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
Customer: ${customer_name#1}
|
||||||
|
Address: ${customer_address#1}
|
||||||
|
|
||||||
|
Customer: ${customer_name#2}
|
||||||
|
Address: ${customer_address#2}
|
||||||
|
|
||||||
|
Customer: ${customer_name#3}
|
||||||
|
Address: ${customer_address#3}
|
||||||
|
|
||||||
|
It is also possible to pass an array with the values to replace the marcros with.
|
||||||
|
If an array with replacements is passed, the ``count`` argument is ignored, it is the size of the array that counts.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$replacements = array(
|
||||||
|
array('customer_name' => 'Batman', 'customer_address' => 'Gotham City'),
|
||||||
|
array('customer_name' => 'Superman', 'customer_address' => 'Metropolis'),
|
||||||
|
);
|
||||||
|
$templateProcessor->cloneBlock('block_name', 0, true, false, $replacements);
|
||||||
|
|
||||||
|
The result will then be
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
Customer: Batman
|
||||||
|
Address: Gotham City
|
||||||
|
|
||||||
|
Customer: Superman
|
||||||
|
Address: Metropolis
|
||||||
|
|
||||||
|
replaceBlock
|
||||||
|
""""""""""""
|
||||||
|
Given a template containing
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
${block_name}
|
||||||
|
This block content will be replaced
|
||||||
|
${/block_name}
|
||||||
|
|
||||||
|
The following will replace everything between``${block_name}`` and ``${/block_name}`` with the value passed.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->replaceBlock('block_name', 'This is the replacement text.');
|
||||||
|
|
||||||
|
deleteBlock
|
||||||
|
"""""""""""
|
||||||
|
Same as previous, but it deletes the block
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->deleteBlock('block_name');
|
||||||
|
|
||||||
|
cloneRow
|
||||||
|
""""""""
|
||||||
|
Clones a table row in a template document.
|
||||||
|
See ``Sample_07_TemplateCloneRow.php`` for an example.
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
+-----------+----------------+
|
||||||
|
| ${userId} | ${userName} |
|
||||||
|
| |----------------+
|
||||||
|
| | ${userAddress} |
|
||||||
|
+-----------+----------------+
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$templateProcessor->cloneRow('userId', 2);
|
||||||
|
|
||||||
|
Will result in
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
+-------------+------------------+
|
||||||
|
| ${userId#1} | ${userName#1} |
|
||||||
|
| |------------------+
|
||||||
|
| | ${userAddress#1} |
|
||||||
|
+-------------+------------------+
|
||||||
|
| ${userId#2} | ${userName#2} |
|
||||||
|
| |------------------+
|
||||||
|
| | ${userAddress#2} |
|
||||||
|
+-------------+------------------+
|
||||||
|
|
||||||
|
cloneRowAndSetValues
|
||||||
|
""""""""""""""""""""
|
||||||
|
Finds a row in a table row identified by `$search` param and clones it as many times as there are entries in `$values`.
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
+-----------+----------------+
|
||||||
|
| ${userId} | ${userName} |
|
||||||
|
| |----------------+
|
||||||
|
| | ${userAddress} |
|
||||||
|
+-----------+----------------+
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$values = [
|
||||||
|
['userId' => 1, 'userName' => 'Batman', 'userAddress' => 'Gotham City'],
|
||||||
|
['userId' => 2, 'userName' => 'Superman', 'userAddress' => 'Metropolis'],
|
||||||
|
];
|
||||||
|
$templateProcessor->cloneRowAndSetValues('userId', );
|
||||||
|
|
||||||
|
Will result in
|
||||||
|
|
||||||
|
.. code-block:: clean
|
||||||
|
|
||||||
|
+---+-------------+
|
||||||
|
| 1 | Batman |
|
||||||
|
| |-------------+
|
||||||
|
| | Gotham City |
|
||||||
|
+---+-------------+
|
||||||
|
| 2 | Superman |
|
||||||
|
| |-------------+
|
||||||
|
| | Metropolis |
|
||||||
|
+---+-------------+
|
||||||
|
|
||||||
|
applyXslStyleSheet
|
||||||
|
""""""""""""""""""
|
||||||
|
Applies the XSL stylesheet passed to header part, footer part and main part
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$xslDomDocument = new \DOMDocument();
|
||||||
|
$xslDomDocument->load('/path/to/my/stylesheet.xsl');
|
||||||
|
$templateProcessor->applyXslStyleSheet($xslDomDocument);
|
||||||
|
|
||||||
|
setComplexValue
|
||||||
|
"""""""""""""""
|
||||||
|
Raplaces a ${macro} with the ComplexType passed.
|
||||||
|
See ``Sample_40_TemplateSetComplexValue.php`` for examples.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$inline = new TextRun();
|
||||||
|
$inline->addText('by a red italic text', array('italic' => true, 'color' => 'red'));
|
||||||
|
$templateProcessor->setComplexValue('inline', $inline);
|
||||||
|
|
||||||
|
setComplexBlock
|
||||||
|
"""""""""""""""
|
||||||
|
Raplaces a ${macro} with the ComplexType passed.
|
||||||
|
See ``Sample_40_TemplateSetComplexValue.php`` for examples.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$table = new Table(array('borderSize' => 12, 'borderColor' => 'green', 'width' => 6000, 'unit' => TblWidth::TWIP));
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell A1');
|
||||||
|
$table->addCell(150)->addText('Cell A2');
|
||||||
|
$table->addCell(150)->addText('Cell A3');
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell B1');
|
||||||
|
$table->addCell(150)->addText('Cell B2');
|
||||||
|
$table->addCell(150)->addText('Cell B3');
|
||||||
|
$templateProcessor->setComplexBlock('table', $table);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
includes:
|
includes:
|
||||||
- vendor/phpstan/phpstan/conf/config.level1.neon
|
- vendor/phpstan/phpstan/conf/config.level1.neon
|
||||||
parameters:
|
parameters:
|
||||||
memory-limit: 200000
|
memory-limit: 20000000
|
||||||
autoload_directories:
|
autoload_directories:
|
||||||
- tests
|
- tests
|
||||||
autoload_files:
|
autoload_files:
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
convertNoticesToExceptions="true"
|
convertNoticesToExceptions="true"
|
||||||
convertWarningsToExceptions="true"
|
convertWarningsToExceptions="true"
|
||||||
processIsolation="false"
|
processIsolation="false"
|
||||||
stopOnFailure="false"
|
stopOnFailure="false">
|
||||||
syntaxCheck="false">
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="PhpWord Test Suite">
|
<testsuite name="PhpWord Test Suite">
|
||||||
<directory>./tests/PhpWord</directory>
|
<directory>./tests/PhpWord</directory>
|
||||||
|
|
@ -22,7 +21,8 @@
|
||||||
</whitelist>
|
</whitelist>
|
||||||
</filter>
|
</filter>
|
||||||
<logging>
|
<logging>
|
||||||
<log type="coverage-html" target="./build/coverage" charset="UTF-8" highlight="true" />
|
<log type="coverage-html" target="./build/coverage" />
|
||||||
<log type="coverage-clover" target="./build/logs/clover.xml" />
|
<log type="coverage-clover" target="./build/logs/clover.xml" />
|
||||||
|
<log type="junit" target="./build/logs/logfile.xml"/>
|
||||||
</logging>
|
</logging>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
use PhpOffice\PhpWord\Style\Font;
|
use PhpOffice\PhpWord\Style\Font;
|
||||||
use PhpOffice\PhpWord\Style\Paragraph;
|
|
||||||
|
|
||||||
include_once 'Sample_Header.php';
|
include_once 'Sample_Header.php';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||||
|
|
||||||
include_once 'Sample_Header.php';
|
include_once 'Sample_Header.php';
|
||||||
|
|
||||||
// New Word Document
|
// New Word Document
|
||||||
|
|
@ -21,6 +23,12 @@ $section = $phpWord->addSection(
|
||||||
);
|
);
|
||||||
$section->addText('This section uses other margins with folio papersize.');
|
$section->addText('This section uses other margins with folio papersize.');
|
||||||
|
|
||||||
|
// The text of this section is vertically centered
|
||||||
|
$section = $phpWord->addSection(
|
||||||
|
array('vAlign' => VerticalJc::CENTER)
|
||||||
|
);
|
||||||
|
$section->addText('This section is vertically centered.');
|
||||||
|
|
||||||
// New portrait section with Header & Footer
|
// New portrait section with Header & Footer
|
||||||
$section = $phpWord->addSection(
|
$section = $phpWord->addSection(
|
||||||
array(
|
array(
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ $textrun->addText(' Sample Object: ');
|
||||||
$textrun->addObject('resources/_sheet.xls');
|
$textrun->addObject('resources/_sheet.xls');
|
||||||
$textrun->addText(' Here is some more text. ');
|
$textrun->addText(' Here is some more text. ');
|
||||||
|
|
||||||
|
$textrun = $section->addTextRun();
|
||||||
|
$textrun->addText('This text is not visible.', array('hidden' => true));
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
||||||
if (!CLI) {
|
if (!CLI) {
|
||||||
|
|
|
||||||
|
|
@ -36,22 +36,46 @@ $templateProcessor->setValue('rowNumber#9', '9');
|
||||||
$templateProcessor->setValue('rowNumber#10', '10');
|
$templateProcessor->setValue('rowNumber#10', '10');
|
||||||
|
|
||||||
// Table with a spanned cell
|
// Table with a spanned cell
|
||||||
$templateProcessor->cloneRow('userId', 3);
|
$values = array(
|
||||||
|
array(
|
||||||
|
'userId' => 1,
|
||||||
|
'userFirstName' => 'James',
|
||||||
|
'userName' => 'Taylor',
|
||||||
|
'userPhone' => '+1 428 889 773',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'userId' => 2,
|
||||||
|
'userFirstName' => 'Robert',
|
||||||
|
'userName' => 'Bell',
|
||||||
|
'userPhone' => '+1 428 889 774',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'userId' => 3,
|
||||||
|
'userFirstName' => 'Michael',
|
||||||
|
'userName' => 'Ray',
|
||||||
|
'userPhone' => '+1 428 889 775',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
$templateProcessor->setValue('userId#1', '1');
|
$templateProcessor->cloneRowAndSetValues('userId', $values);
|
||||||
$templateProcessor->setValue('userFirstName#1', 'James');
|
|
||||||
$templateProcessor->setValue('userName#1', 'Taylor');
|
|
||||||
$templateProcessor->setValue('userPhone#1', '+1 428 889 773');
|
|
||||||
|
|
||||||
$templateProcessor->setValue('userId#2', '2');
|
//this is equivalent to cloning and settings values with cloneRowAndSetValues
|
||||||
$templateProcessor->setValue('userFirstName#2', 'Robert');
|
// $templateProcessor->cloneRow('userId', 3);
|
||||||
$templateProcessor->setValue('userName#2', 'Bell');
|
|
||||||
$templateProcessor->setValue('userPhone#2', '+1 428 889 774');
|
|
||||||
|
|
||||||
$templateProcessor->setValue('userId#3', '3');
|
// $templateProcessor->setValue('userId#1', '1');
|
||||||
$templateProcessor->setValue('userFirstName#3', 'Michael');
|
// $templateProcessor->setValue('userFirstName#1', 'James');
|
||||||
$templateProcessor->setValue('userName#3', 'Ray');
|
// $templateProcessor->setValue('userName#1', 'Taylor');
|
||||||
$templateProcessor->setValue('userPhone#3', '+1 428 889 775');
|
// $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;
|
echo date('H:i:s'), ' Saving the result document...', EOL;
|
||||||
$templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx');
|
$templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx');
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,8 @@ $source = "resources/{$name}.doc";
|
||||||
echo date('H:i:s'), " Reading contents from `{$source}`", EOL;
|
echo date('H:i:s'), " Reading contents from `{$source}`", EOL;
|
||||||
$phpWord = \PhpOffice\PhpWord\IOFactory::load($source, 'MsDoc');
|
$phpWord = \PhpOffice\PhpWord\IOFactory::load($source, 'MsDoc');
|
||||||
|
|
||||||
// (Re)write contents
|
// Save file
|
||||||
$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf');
|
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
||||||
foreach ($writers as $writer => $extension) {
|
if (!CLI) {
|
||||||
echo date('H:i:s'), " Write to {$writer} format", EOL;
|
include_once 'Sample_Footer.php';
|
||||||
$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer);
|
|
||||||
$xmlWriter->save("{$name}.{$extension}");
|
|
||||||
rename("{$name}.{$extension}", "results/{$name}.{$extension}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
include_once 'Sample_Footer.php';
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ $section->addText('List with inline formatting.');
|
||||||
$listItemRun = $section->addListItemRun();
|
$listItemRun = $section->addListItemRun();
|
||||||
$listItemRun->addText('List item 1');
|
$listItemRun->addText('List item 1');
|
||||||
$listItemRun->addText(' in bold', array('bold' => true));
|
$listItemRun->addText(' in bold', array('bold' => true));
|
||||||
$listItemRun = $section->addListItemRun();
|
$listItemRun = $section->addListItemRun(1, $predefinedMultilevelStyle, $paragraphStyleName);
|
||||||
$listItemRun->addText('List item 2');
|
$listItemRun->addText('List item 2');
|
||||||
$listItemRun->addText(' in italic', array('italic' => true));
|
$listItemRun->addText(' in italic', array('italic' => true));
|
||||||
$footnote = $listItemRun->addFootnote();
|
$footnote = $listItemRun->addFootnote();
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ $templateProcessor->deleteBlock('DELETEME');
|
||||||
echo date('H:i:s'), ' Saving the result document...', EOL;
|
echo date('H:i:s'), ' Saving the result document...', EOL;
|
||||||
$templateProcessor->saveAs('results/Sample_23_TemplateBlock.docx');
|
$templateProcessor->saveAs('results/Sample_23_TemplateBlock.docx');
|
||||||
|
|
||||||
echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_23_TemplateBlock.docx');
|
echo getEndingNotes(array('Word2007' => 'docx'), 'Sample_23_TemplateBlock');
|
||||||
if (!CLI) {
|
if (!CLI) {
|
||||||
include_once 'Sample_Footer.php';
|
include_once 'Sample_Footer.php';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ $html .= '<table align="center" style="width: 50%; border: 6px #0000FF double;">
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td style="border-style: dotted;">1</td><td colspan="2">2</td></tr>
|
<tr><td style="border-style: dotted; border-color: #FF0000">1</td><td colspan="2">2</td></tr>
|
||||||
<tr><td>This is <b>bold</b> text</td><td></td><td>6</td></tr>
|
<tr><td>This is <b>bold</b> text</td><td></td><td>6</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>';
|
</table>';
|
||||||
|
|
@ -89,6 +89,9 @@ $html .= '<table align="center" style="width: 80%; border: 6px #0000FF double;">
|
||||||
<tr><td style="text-align: center;">Cell in parent table</td></tr>
|
<tr><td style="text-align: center;">Cell in parent table</td></tr>
|
||||||
</table>';
|
</table>';
|
||||||
|
|
||||||
|
$html .= '<p style="margin-top: 240pt;">The text below is not visible, click on show/hide to reveil it:</p>';
|
||||||
|
$html .= '<p style="display: none">This is hidden text</p>';
|
||||||
|
|
||||||
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html, false, false);
|
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html, false, false);
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,29 @@ $textrun->addText('This is a Left to Right paragraph.');
|
||||||
$textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END));
|
$textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END));
|
||||||
$textrun->addText('سلام این یک پاراگراف راست به چپ است', array('rtl' => true));
|
$textrun->addText('سلام این یک پاراگراف راست به چپ است', array('rtl' => true));
|
||||||
|
|
||||||
|
$section->addText('Table visually presented as RTL');
|
||||||
|
$style = array('rtl' => true, 'size' => 12);
|
||||||
|
$tableStyle = array('borderSize' => 6, 'borderColor' => '000000', 'width' => 5000, 'unit' => \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT, 'bidiVisual' => true);
|
||||||
|
|
||||||
|
$table = $section->addTable($tableStyle);
|
||||||
|
$cellHCentered = array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER);
|
||||||
|
$cellHEnd = array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END);
|
||||||
|
$cellVCentered = array('valign' => \PhpOffice\PhpWord\Style\Cell::VALIGN_CENTER);
|
||||||
|
|
||||||
|
//Vidually bidirectinal table
|
||||||
|
$table->addRow();
|
||||||
|
$cell = $table->addCell(500, $cellVCentered);
|
||||||
|
$textrun = $cell->addTextRun($cellHCentered);
|
||||||
|
$textrun->addText('ردیف', $style);
|
||||||
|
|
||||||
|
$cell = $table->addCell(11000);
|
||||||
|
$textrun = $cell->addTextRun($cellHEnd);
|
||||||
|
$textrun->addText('سوالات', $style);
|
||||||
|
|
||||||
|
$cell = $table->addCell(500, $cellVCentered);
|
||||||
|
$textrun = $cell->addTextRun($cellHCentered);
|
||||||
|
$textrun->addText('بارم', $style);
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
||||||
if (!CLI) {
|
if (!CLI) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
use PhpOffice\PhpWord\Element\Field;
|
||||||
|
use PhpOffice\PhpWord\Element\Table;
|
||||||
|
use PhpOffice\PhpWord\Element\TextRun;
|
||||||
|
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
||||||
|
|
||||||
|
include_once 'Sample_Header.php';
|
||||||
|
|
||||||
|
// Template processor instance creation
|
||||||
|
echo date('H:i:s'), ' Creating new TemplateProcessor instance...', EOL;
|
||||||
|
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('resources/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
|
||||||
|
$title = new TextRun();
|
||||||
|
$title->addText('This title has been set ', array('bold' => true, 'italic' => true, 'color' => 'blue'));
|
||||||
|
$title->addText('dynamically', array('bold' => true, 'italic' => true, 'color' => 'red', 'underline' => 'single'));
|
||||||
|
$templateProcessor->setComplexBlock('title', $title);
|
||||||
|
|
||||||
|
$inline = new TextRun();
|
||||||
|
$inline->addText('by a red italic text', array('italic' => true, 'color' => 'red'));
|
||||||
|
$templateProcessor->setComplexValue('inline', $inline);
|
||||||
|
|
||||||
|
$table = new Table(array('borderSize' => 12, 'borderColor' => 'green', 'width' => 6000, 'unit' => TblWidth::TWIP));
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell A1');
|
||||||
|
$table->addCell(150)->addText('Cell A2');
|
||||||
|
$table->addCell(150)->addText('Cell A3');
|
||||||
|
$table->addRow();
|
||||||
|
$table->addCell(150)->addText('Cell B1');
|
||||||
|
$table->addCell(150)->addText('Cell B2');
|
||||||
|
$table->addCell(150)->addText('Cell B3');
|
||||||
|
$templateProcessor->setComplexBlock('table', $table);
|
||||||
|
|
||||||
|
$field = new Field('DATE', array('dateformat' => 'dddd d MMMM yyyy H:mm:ss'), array('PreserveFormat'));
|
||||||
|
$templateProcessor->setComplexValue('field', $field);
|
||||||
|
|
||||||
|
// $link = new Link('https://github.com/PHPOffice/PHPWord');
|
||||||
|
// $templateProcessor->setComplexValue('link', $link);
|
||||||
|
|
||||||
|
echo date('H:i:s'), ' Saving the result document...', EOL;
|
||||||
|
$templateProcessor->saveAs('results/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
|
||||||
|
echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_40_TemplateSetComplexValue.docx');
|
||||||
|
if (!CLI) {
|
||||||
|
include_once 'Sample_Footer.php';
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ if (!CLI) {
|
||||||
<a class="btn btn-lg btn-primary" href="http://phpword.readthedocs.org/" role="button"><i class="fa fa-book fa-lg" title="Docs"></i> Read the Docs</a>
|
<a class="btn btn-lg btn-primary" href="http://phpword.readthedocs.org/" role="button"><i class="fa fa-book fa-lg" title="Docs"></i> Read the Docs</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if (!CLI) {
|
if (!CLI) {
|
||||||
echo '<h3>Requirement check:</h3>';
|
echo '<h3>Requirement check:</h3>';
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -11,5 +11,15 @@
|
||||||
<ul><li>Item 1</li><li>Item 2</li><ul><li>Item 2.1</li><li>Item 2.1</li></ul></ul>
|
<ul><li>Item 1</li><li>Item 2</li><ul><li>Item 2.1</li><li>Item 2.1</li></ul></ul>
|
||||||
<p>Ordered (numbered) list:</p>
|
<p>Ordered (numbered) list:</p>
|
||||||
<ol><li>Item 1</li><li>Item 2</li></ol>
|
<ol><li>Item 1</li><li>Item 2</li></ol>
|
||||||
|
|
||||||
|
<p style="line-height:2">Double height</p>
|
||||||
|
|
||||||
|
<h2>Includes images</h2>
|
||||||
|
<img src="https://phpword.readthedocs.io/en/latest/_images/phpword.png" alt=""/>
|
||||||
|
|
||||||
|
<img src="http://php.net/images/logos/php-med-trans-light.gif" name="Imagen 12" align="bottom" width="208" height="183" border="0"/>
|
||||||
|
<img src="http://php.net/images/logos/php-icon.png" name="Imagen 13" align="bottom" width="143" height="202" border="0"/>
|
||||||
|
<img src="http://php.net/images/logos/php-med-trans-light.gif" name="Imagen 14" align="bottom" width="194" height="188" border="0"/>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -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
|
||||||
|
|
@ -31,11 +31,11 @@ namespace PhpOffice\PhpWord\Element;
|
||||||
* @method Footnote addFootnote(mixed $pStyle = null)
|
* @method Footnote addFootnote(mixed $pStyle = null)
|
||||||
* @method Endnote addEndnote(mixed $pStyle = null)
|
* @method Endnote addEndnote(mixed $pStyle = null)
|
||||||
* @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null)
|
* @method CheckBox addCheckBox(string $name, $text, mixed $fStyle = null, mixed $pStyle = null)
|
||||||
* @method Title addTitle(string $text, int $depth = 1)
|
* @method Title addTitle(mixed $text, int $depth = 1)
|
||||||
* @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9)
|
* @method TOC addTOC(mixed $fontStyle = null, mixed $tocStyle = null, int $minDepth = 1, int $maxDepth = 9)
|
||||||
* @method PageBreak addPageBreak()
|
* @method PageBreak addPageBreak()
|
||||||
* @method Table addTable(mixed $style = null)
|
* @method Table addTable(mixed $style = null)
|
||||||
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false)
|
* @method Image addImage(string $source, mixed $style = null, bool $isWatermark = false, $name = null)
|
||||||
* @method OLEObject addOLEObject(string $source, mixed $style = null)
|
* @method OLEObject addOLEObject(string $source, mixed $style = null)
|
||||||
* @method TextBox addTextBox(mixed $style = null)
|
* @method TextBox addTextBox(mixed $style = null)
|
||||||
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
|
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ abstract class AbstractElement
|
||||||
*
|
*
|
||||||
* @param \PhpOffice\PhpWord\Element\AbstractElement $container
|
* @param \PhpOffice\PhpWord\Element\AbstractElement $container
|
||||||
*/
|
*/
|
||||||
public function setParentContainer(AbstractElement $container)
|
public function setParentContainer(self $container)
|
||||||
{
|
{
|
||||||
$this->parentContainer = substr(get_class($container), strrpos(get_class($container), '\\') + 1);
|
$this->parentContainer = substr(get_class($container), strrpos(get_class($container), '\\') + 1);
|
||||||
$this->parent = $container;
|
$this->parent = $container;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,13 @@ class Image extends AbstractElement
|
||||||
*/
|
*/
|
||||||
private $watermark;
|
private $watermark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of image
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image type
|
* Image type
|
||||||
*
|
*
|
||||||
|
|
@ -127,15 +134,17 @@ class Image extends AbstractElement
|
||||||
* @param string $source
|
* @param string $source
|
||||||
* @param mixed $style
|
* @param mixed $style
|
||||||
* @param bool $watermark
|
* @param bool $watermark
|
||||||
|
* @param string $name
|
||||||
*
|
*
|
||||||
* @throws \PhpOffice\PhpWord\Exception\InvalidImageException
|
* @throws \PhpOffice\PhpWord\Exception\InvalidImageException
|
||||||
* @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException
|
* @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->source = $source;
|
||||||
$this->setIsWatermark($watermark);
|
|
||||||
$this->style = $this->setNewStyle(new ImageStyle(), $style, true);
|
$this->style = $this->setNewStyle(new ImageStyle(), $style, true);
|
||||||
|
$this->setIsWatermark($watermark);
|
||||||
|
$this->setName($name);
|
||||||
|
|
||||||
$this->checkImage();
|
$this->checkImage();
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +179,26 @@ class Image extends AbstractElement
|
||||||
return $this->sourceType;
|
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
|
* Get image media ID
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ class OLEObject extends AbstractElement
|
||||||
$this->style = $this->setNewStyle(new ImageStyle(), $style, true);
|
$this->style = $this->setNewStyle(new ImageStyle(), $style, true);
|
||||||
$this->icon = realpath(__DIR__ . "/../resources/{$ext}.png");
|
$this->icon = realpath(__DIR__ . "/../resources/{$ext}.png");
|
||||||
|
|
||||||
return $this;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidObjectException();
|
throw new InvalidObjectException();
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class PreserveText extends AbstractElement
|
||||||
/**
|
/**
|
||||||
* Text content
|
* Text content
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|array
|
||||||
*/
|
*/
|
||||||
private $text;
|
private $text;
|
||||||
|
|
||||||
|
|
@ -64,8 +64,6 @@ class PreserveText extends AbstractElement
|
||||||
if (isset($matches[0])) {
|
if (isset($matches[0])) {
|
||||||
$this->text = $matches;
|
$this->text = $matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -91,7 +89,7 @@ class PreserveText extends AbstractElement
|
||||||
/**
|
/**
|
||||||
* Get Text content
|
* Get Text content
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string|array
|
||||||
*/
|
*/
|
||||||
public function getText()
|
public function getText()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ class SDT extends Text
|
||||||
*/
|
*/
|
||||||
public function setType($value)
|
public function setType($value)
|
||||||
{
|
{
|
||||||
$enum = array('comboBox', 'dropDownList', 'date');
|
$enum = array('plainText', 'comboBox', 'dropDownList', 'date');
|
||||||
$this->type = $this->setEnumVal($value, $enum, 'comboBox');
|
$this->type = $this->setEnumVal($value, $enum, 'comboBox');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -59,14 +59,16 @@ class Section extends AbstractContainer
|
||||||
* Create new instance
|
* Create new instance
|
||||||
*
|
*
|
||||||
* @param int $sectionCount
|
* @param int $sectionCount
|
||||||
* @param array $style
|
* @param null|array|\PhpOffice\PhpWord\Style $style
|
||||||
*/
|
*/
|
||||||
public function __construct($sectionCount, $style = null)
|
public function __construct($sectionCount, $style = null)
|
||||||
{
|
{
|
||||||
$this->sectionId = $sectionCount;
|
$this->sectionId = $sectionCount;
|
||||||
$this->setDocPart($this->container, $this->sectionId);
|
$this->setDocPart($this->container, $this->sectionId);
|
||||||
$this->style = new SectionStyle();
|
if (null === $style) {
|
||||||
$this->setStyle($style);
|
$style = new SectionStyle();
|
||||||
|
}
|
||||||
|
$this->style = $this->setNewStyle(new SectionStyle(), $style);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -135,18 +135,40 @@ class Table extends AbstractElement
|
||||||
public function countColumns()
|
public function countColumns()
|
||||||
{
|
{
|
||||||
$columnCount = 0;
|
$columnCount = 0;
|
||||||
if (is_array($this->rows)) {
|
|
||||||
$rowCount = count($this->rows);
|
$rowCount = count($this->rows);
|
||||||
for ($i = 0; $i < $rowCount; $i++) {
|
for ($i = 0; $i < $rowCount; $i++) {
|
||||||
/** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
|
/** @var \PhpOffice\PhpWord\Element\Row $row Type hint */
|
||||||
$row = $this->rows[$i];
|
$row = $this->rows[$i];
|
||||||
$cellCount = count($row->getCells());
|
$cellCount = count($row->getCells());
|
||||||
if ($columnCount < $cellCount) {
|
if ($columnCount < $cellCount) {
|
||||||
$columnCount = $cellCount;
|
$columnCount = $cellCount;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $columnCount;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,12 @@ class Title extends AbstractElement
|
||||||
*/
|
*/
|
||||||
public function __construct($text, $depth = 1)
|
public function __construct($text, $depth = 1)
|
||||||
{
|
{
|
||||||
if (isset($text)) {
|
if (is_string($text)) {
|
||||||
if (is_string($text)) {
|
$this->text = CommonText::toUTF8($text);
|
||||||
$this->text = CommonText::toUTF8($text);
|
} elseif ($text instanceof TextRun) {
|
||||||
} elseif ($text instanceof TextRun) {
|
$this->text = $text;
|
||||||
$this->text = $text;
|
} else {
|
||||||
} else {
|
throw new \InvalidArgumentException('Invalid text, should be a string or a TextRun');
|
||||||
throw new \InvalidArgumentException('Invalid text, should be a string or a TextRun');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->depth = $depth;
|
$this->depth = $depth;
|
||||||
|
|
@ -76,8 +74,6 @@ class Title extends AbstractElement
|
||||||
if (array_key_exists($styleName, Style::getStyles())) {
|
if (array_key_exists($styleName, Style::getStyles())) {
|
||||||
$this->style = str_replace('_', '', $styleName);
|
$this->style = str_replace('_', '', $styleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,13 @@ class TrackChange extends AbstractContainer
|
||||||
*
|
*
|
||||||
* @param string $changeType
|
* @param string $changeType
|
||||||
* @param string $author
|
* @param string $author
|
||||||
* @param null|int|\DateTime $date
|
* @param null|int|bool|\DateTime $date
|
||||||
*/
|
*/
|
||||||
public function __construct($changeType = null, $author = null, $date = null)
|
public function __construct($changeType = null, $author = null, $date = null)
|
||||||
{
|
{
|
||||||
$this->changeType = $changeType;
|
$this->changeType = $changeType;
|
||||||
$this->author = $author;
|
$this->author = $author;
|
||||||
if ($date !== null) {
|
if ($date !== null && $date !== false) {
|
||||||
$this->date = ($date instanceof \DateTime) ? $date : new \DateTime('@' . $date);
|
$this->date = ($date instanceof \DateTime) ? $date : new \DateTime('@' . $date);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ class DocInfo
|
||||||
case 'date': // Date
|
case 'date': // Date
|
||||||
return strtotime($propertyValue);
|
return strtotime($propertyValue);
|
||||||
case 'bool': // Boolean
|
case 'bool': // Boolean
|
||||||
return ($propertyValue == 'true') ? true : false;
|
return $propertyValue == 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $propertyValue;
|
return $propertyValue;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Metadata;
|
namespace PhpOffice\PhpWord\Metadata;
|
||||||
|
|
||||||
use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
|
use PhpOffice\Common\Microsoft\PasswordEncoder;
|
||||||
use PhpOffice\PhpWord\SimpleType\DocProtect;
|
use PhpOffice\PhpWord\SimpleType\DocProtect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,7 +113,7 @@ class Protection
|
||||||
/**
|
/**
|
||||||
* Set password
|
* Set password
|
||||||
*
|
*
|
||||||
* @param $password
|
* @param string $password
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setPassword($password)
|
public function setPassword($password)
|
||||||
|
|
@ -136,7 +136,7 @@ class Protection
|
||||||
/**
|
/**
|
||||||
* Set count for hash iterations
|
* Set count for hash iterations
|
||||||
*
|
*
|
||||||
* @param $spinCount
|
* @param int $spinCount
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setSpinCount($spinCount)
|
public function setSpinCount($spinCount)
|
||||||
|
|
@ -159,7 +159,7 @@ class Protection
|
||||||
/**
|
/**
|
||||||
* Set algorithm
|
* Set algorithm
|
||||||
*
|
*
|
||||||
* @param $algorithm
|
* @param string $algorithm
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setAlgorithm($algorithm)
|
public function setAlgorithm($algorithm)
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ use PhpOffice\PhpWord\Exception\Exception;
|
||||||
* @method int addChart(Element\Chart $chart)
|
* @method int addChart(Element\Chart $chart)
|
||||||
* @method int addComment(Element\Comment $comment)
|
* @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 addFontStyle(string $styleName, mixed $fontStyle, mixed $paragraphStyle = null)
|
||||||
* @method Style\Font addLinkStyle(string $styleName, mixed $styles)
|
* @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\Table addTableStyle(string $styleName, mixed $styleTable, mixed $styleFirstRow = null)
|
||||||
* @method Style\Numbering addNumberingStyle(string $styleName, mixed $styles)
|
* @method Style\Numbering addNumberingStyle(string $styleName, mixed $styles)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1619,7 +1619,7 @@ class MsDoc extends AbstractReader implements ReaderInterface
|
||||||
break;
|
break;
|
||||||
// sprmCFData
|
// sprmCFData
|
||||||
case 0x06:
|
case 0x06:
|
||||||
$sprmCFData = dechex($operand) == 0x00 ? false : true;
|
$sprmCFData = dechex($operand) != 0x00;
|
||||||
break;
|
break;
|
||||||
// sprmCFItalic
|
// sprmCFItalic
|
||||||
case 0x36:
|
case 0x36:
|
||||||
|
|
@ -2185,6 +2185,8 @@ class MsDoc extends AbstractReader implements ReaderInterface
|
||||||
|
|
||||||
$sprmCPicLocation += $embeddedBlipRH['recLen'];
|
$sprmCPicLocation += $embeddedBlipRH['recLen'];
|
||||||
break;
|
break;
|
||||||
|
case self::OFFICEARTBLIPPNG:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// print_r(dechex($embeddedBlipRH['recType']));
|
// print_r(dechex($embeddedBlipRH['recType']));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,9 @@ class Word2007 extends AbstractReader implements ReaderInterface
|
||||||
foreach ($steps as $step) {
|
foreach ($steps as $step) {
|
||||||
$stepPart = $step['stepPart'];
|
$stepPart = $step['stepPart'];
|
||||||
$stepItems = $step['stepItems'];
|
$stepItems = $step['stepItems'];
|
||||||
|
if (!isset($relationships[$stepPart])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foreach ($relationships[$stepPart] as $relItem) {
|
foreach ($relationships[$stepPart] as $relItem) {
|
||||||
$relType = $relItem['type'];
|
$relType = $relItem['type'];
|
||||||
if (isset($stepItems[$relType])) {
|
if (isset($stepItems[$relType])) {
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,20 @@ abstract class AbstractPart
|
||||||
}
|
}
|
||||||
$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') {
|
} elseif ($node->nodeName == 'w:object') {
|
||||||
// Object
|
// Object
|
||||||
$rId = $xmlReader->getAttribute('r:id', $node, 'o:OLEObject');
|
$rId = $xmlReader->getAttribute('r:id', $node, 'o:OLEObject');
|
||||||
|
|
@ -283,6 +297,8 @@ abstract class AbstractPart
|
||||||
$target = $this->getMediaTarget($docPart, $rId);
|
$target = $this->getMediaTarget($docPart, $rId);
|
||||||
if (!is_null($target)) {
|
if (!is_null($target)) {
|
||||||
$parent->addLink($target, $textContent, $fontStyle, $paragraphStyle);
|
$parent->addLink($target, $textContent, $fontStyle, $paragraphStyle);
|
||||||
|
} else {
|
||||||
|
$parent->addText($textContent, $fontStyle, $paragraphStyle);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/** @var AbstractElement $element */
|
/** @var AbstractElement $element */
|
||||||
|
|
@ -322,7 +338,7 @@ abstract class AbstractPart
|
||||||
} elseif ('w:tr' == $tblNode->nodeName) { // Row
|
} elseif ('w:tr' == $tblNode->nodeName) { // Row
|
||||||
$rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight');
|
$rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight');
|
||||||
$rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight');
|
$rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight');
|
||||||
$rowHRule = $rowHRule == 'exact' ? true : false;
|
$rowHRule = $rowHRule == 'exact';
|
||||||
$rowStyle = array(
|
$rowStyle = array(
|
||||||
'tblHeader' => $xmlReader->elementExists('w:trPr/w:tblHeader', $tblNode),
|
'tblHeader' => $xmlReader->elementExists('w:trPr/w:tblHeader', $tblNode),
|
||||||
'cantSplit' => $xmlReader->elementExists('w:trPr/w:cantSplit', $tblNode),
|
'cantSplit' => $xmlReader->elementExists('w:trPr/w:cantSplit', $tblNode),
|
||||||
|
|
@ -430,6 +446,7 @@ abstract class AbstractPart
|
||||||
'rtl' => array(self::READ_TRUE, 'w:rtl'),
|
'rtl' => array(self::READ_TRUE, 'w:rtl'),
|
||||||
'lang' => array(self::READ_VALUE, 'w:lang'),
|
'lang' => array(self::READ_VALUE, 'w:lang'),
|
||||||
'position' => array(self::READ_VALUE, 'w:position'),
|
'position' => array(self::READ_VALUE, 'w:position'),
|
||||||
|
'hidden' => array(self::READ_TRUE, 'w:vanish'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
return $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
||||||
|
|
@ -466,6 +483,7 @@ abstract class AbstractPart
|
||||||
$styleDefs["border{$ucfSide}Style"] = array(self::READ_VALUE, "w:tblBorders/w:$side", 'w:val');
|
$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['layout'] = array(self::READ_VALUE, 'w:tblLayout', 'w:type');
|
||||||
|
$styleDefs['bidiVisual'] = array(self::READ_TRUE, 'w:bidiVisual');
|
||||||
$styleDefs['cellSpacing'] = array(self::READ_VALUE, 'w:tblCellSpacing', 'w:w');
|
$styleDefs['cellSpacing'] = array(self::READ_VALUE, 'w:tblCellSpacing', 'w:w');
|
||||||
$style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
$style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ class Document extends AbstractPart
|
||||||
{
|
{
|
||||||
$styleDefs = array(
|
$styleDefs = array(
|
||||||
'breakType' => array(self::READ_VALUE, 'w:type'),
|
'breakType' => array(self::READ_VALUE, 'w:type'),
|
||||||
|
'vAlign' => array(self::READ_VALUE, 'w:vAlign'),
|
||||||
'pageSizeW' => array(self::READ_VALUE, 'w:pgSz', 'w:w'),
|
'pageSizeW' => array(self::READ_VALUE, 'w:pgSz', 'w:w'),
|
||||||
'pageSizeH' => array(self::READ_VALUE, 'w:pgSz', 'w:h'),
|
'pageSizeH' => array(self::READ_VALUE, 'w:pgSz', 'w:h'),
|
||||||
'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'),
|
'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'),
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ class Settings extends AbstractPart
|
||||||
*
|
*
|
||||||
* @param XMLReader $xmlReader
|
* @param XMLReader $xmlReader
|
||||||
* @param PhpWord $phpWord
|
* @param PhpWord $phpWord
|
||||||
* @param \DOMNode $node
|
* @param \DOMElement $node
|
||||||
*/
|
*/
|
||||||
protected function setThemeFontLang(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
protected function setThemeFontLang(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
||||||
{
|
{
|
||||||
|
|
@ -102,7 +102,7 @@ class Settings extends AbstractPart
|
||||||
*
|
*
|
||||||
* @param XMLReader $xmlReader
|
* @param XMLReader $xmlReader
|
||||||
* @param PhpWord $phpWord
|
* @param PhpWord $phpWord
|
||||||
* @param \DOMNode $node
|
* @param \DOMElement $node
|
||||||
*/
|
*/
|
||||||
protected function setDocumentProtection(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
protected function setDocumentProtection(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
||||||
{
|
{
|
||||||
|
|
@ -119,7 +119,7 @@ class Settings extends AbstractPart
|
||||||
*
|
*
|
||||||
* @param XMLReader $xmlReader
|
* @param XMLReader $xmlReader
|
||||||
* @param PhpWord $phpWord
|
* @param PhpWord $phpWord
|
||||||
* @param \DOMNode $node
|
* @param \DOMElement $node
|
||||||
*/
|
*/
|
||||||
protected function setProofState(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
protected function setProofState(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +141,7 @@ class Settings extends AbstractPart
|
||||||
*
|
*
|
||||||
* @param XMLReader $xmlReader
|
* @param XMLReader $xmlReader
|
||||||
* @param PhpWord $phpWord
|
* @param PhpWord $phpWord
|
||||||
* @param \DOMNode $node
|
* @param \DOMElement $node
|
||||||
*/
|
*/
|
||||||
protected function setZoom(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
protected function setZoom(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
||||||
{
|
{
|
||||||
|
|
@ -158,7 +158,7 @@ class Settings extends AbstractPart
|
||||||
*
|
*
|
||||||
* @param XMLReader $xmlReader
|
* @param XMLReader $xmlReader
|
||||||
* @param PhpWord $phpWord
|
* @param PhpWord $phpWord
|
||||||
* @param \DOMNode $node
|
* @param \DOMElement $node
|
||||||
*/
|
*/
|
||||||
protected function setRevisionView(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
protected function setRevisionView(XMLReader $xmlReader, PhpWord $phpWord, \DOMElement $node)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,12 @@ class Styles extends AbstractPart
|
||||||
if ($nodes->length > 0) {
|
if ($nodes->length > 0) {
|
||||||
foreach ($nodes as $node) {
|
foreach ($nodes as $node) {
|
||||||
$type = $xmlReader->getAttribute('w:type', $node);
|
$type = $xmlReader->getAttribute('w:type', $node);
|
||||||
$name = $xmlReader->getAttribute('w:styleId', $node);
|
$name = $xmlReader->getAttribute('w:val', $node, 'w:name');
|
||||||
if (is_null($name)) {
|
if (is_null($name)) {
|
||||||
$name = $xmlReader->getAttribute('w:val', $node, 'w:name');
|
$name = $xmlReader->getAttribute('w:styleId', $node);
|
||||||
}
|
}
|
||||||
preg_match('/Heading(\d)/', $name, $headingMatches);
|
$headingMatches = array();
|
||||||
|
preg_match('/Heading\s*(\d)/i', $name, $headingMatches);
|
||||||
// $default = ($xmlReader->getAttribute('w:default', $node) == 1);
|
// $default = ($xmlReader->getAttribute('w:default', $node) == 1);
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'paragraph':
|
case 'paragraph':
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ class Converter
|
||||||
/**
|
/**
|
||||||
* Convert point to pixel
|
* Convert point to pixel
|
||||||
*
|
*
|
||||||
* @param int $point
|
* @param float $point
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public static function pointToPixel($point = 1)
|
public static function pointToPixel($point = 1)
|
||||||
|
|
@ -217,6 +217,17 @@ class Converter
|
||||||
return round($point / self::INCH_TO_POINT * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
|
return round($point / self::INCH_TO_POINT * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert point to cm
|
||||||
|
*
|
||||||
|
* @param float $point
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function pointToCm($point = 1)
|
||||||
|
{
|
||||||
|
return $point / self::INCH_TO_POINT * self::INCH_TO_CM;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert EMU to pixel
|
* Convert EMU to pixel
|
||||||
*
|
*
|
||||||
|
|
@ -299,6 +310,7 @@ class Converter
|
||||||
if ($value == '0') {
|
if ($value == '0') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
$matches = array();
|
||||||
if (preg_match('/^[+-]?([0-9]+\.?[0-9]*)?(px|em|ex|%|in|cm|mm|pt|pc)$/i', $value, $matches)) {
|
if (preg_match('/^[+-]?([0-9]+\.?[0-9]*)?(px|em|ex|%|in|cm|mm|pt|pc)$/i', $value, $matches)) {
|
||||||
$size = $matches[1];
|
$size = $matches[1];
|
||||||
$unit = $matches[2];
|
$unit = $matches[2];
|
||||||
|
|
@ -332,4 +344,37 @@ class Converter
|
||||||
{
|
{
|
||||||
return self::pointToTwip(self::cssToPoint($value));
|
return self::pointToTwip(self::cssToPoint($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a size in CSS format (eg. 10px, 10px, ...) to pixel
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function cssToPixel($value)
|
||||||
|
{
|
||||||
|
return self::pointToPixel(self::cssToPoint($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a size in CSS format (eg. 10px, 10px, ...) to cm
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function cssToCm($value)
|
||||||
|
{
|
||||||
|
return self::pointToCm(self::cssToPoint($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a size in CSS format (eg. 10px, 10px, ...) to emu
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function cssToEmu($value)
|
||||||
|
{
|
||||||
|
return self::pointToEmu(self::cssToPoint($value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ namespace PhpOffice\PhpWord\Shared;
|
||||||
use PhpOffice\PhpWord\Element\AbstractContainer;
|
use PhpOffice\PhpWord\Element\AbstractContainer;
|
||||||
use PhpOffice\PhpWord\Element\Row;
|
use PhpOffice\PhpWord\Element\Row;
|
||||||
use PhpOffice\PhpWord\Element\Table;
|
use PhpOffice\PhpWord\Element\Table;
|
||||||
|
use PhpOffice\PhpWord\Settings;
|
||||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||||
use PhpOffice\PhpWord\SimpleType\NumberFormat;
|
use PhpOffice\PhpWord\SimpleType\NumberFormat;
|
||||||
|
use PhpOffice\PhpWord\Style\Paragraph;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common Html functions
|
* Common Html functions
|
||||||
|
|
@ -32,6 +34,7 @@ class Html
|
||||||
{
|
{
|
||||||
private static $listIndex = 0;
|
private static $listIndex = 0;
|
||||||
private static $xpath;
|
private static $xpath;
|
||||||
|
private static $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add HTML parts.
|
* Add HTML parts.
|
||||||
|
|
@ -44,13 +47,17 @@ class Html
|
||||||
* @param string $html The code to parse
|
* @param string $html The code to parse
|
||||||
* @param bool $fullHTML If it's a full HTML, no need to add 'body' tag
|
* @param bool $fullHTML If it's a full HTML, no need to add 'body' tag
|
||||||
* @param bool $preserveWhiteSpace If false, the whitespaces between nodes will be removed
|
* @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, $preserveWhiteSpace = true)
|
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,
|
* @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.
|
* which could be applied when such an element occurs in the parseNode function.
|
||||||
*/
|
*/
|
||||||
|
self::$options = $options;
|
||||||
|
|
||||||
// Preprocess: remove all line ends, decode HTML entity,
|
// Preprocess: remove all line ends, decode HTML entity,
|
||||||
// fix ampersand and angle brackets and add body tag for HTML fragments
|
// fix ampersand and angle brackets and add body tag for HTML fragments
|
||||||
|
|
@ -65,10 +72,11 @@ class Html
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load DOM
|
// Load DOM
|
||||||
|
libxml_disable_entity_loader(true);
|
||||||
$dom = new \DOMDocument();
|
$dom = new \DOMDocument();
|
||||||
$dom->preserveWhiteSpace = $preserveWhiteSpace;
|
$dom->preserveWhiteSpace = $preserveWhiteSpace;
|
||||||
$dom->loadXML($html);
|
$dom->loadXML($html);
|
||||||
self::$xpath = new \DOMXpath($dom);
|
self::$xpath = new \DOMXPath($dom);
|
||||||
$node = $dom->getElementsByTagName('body');
|
$node = $dom->getElementsByTagName('body');
|
||||||
|
|
||||||
self::parseNode($node->item(0), $element);
|
self::parseNode($node->item(0), $element);
|
||||||
|
|
@ -141,6 +149,7 @@ class Html
|
||||||
'sup' => array('Property', null, null, $styles, null, 'superScript', true),
|
'sup' => array('Property', null, null, $styles, null, 'superScript', true),
|
||||||
'sub' => array('Property', null, null, $styles, null, 'subScript', true),
|
'sub' => array('Property', null, null, $styles, null, 'subScript', true),
|
||||||
'span' => array('Span', $node, null, $styles, null, null, 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),
|
'table' => array('Table', $node, $element, $styles, null, null, null),
|
||||||
'tr' => array('Row', $node, $element, $styles, null, null, null),
|
'tr' => array('Row', $node, $element, $styles, null, null, null),
|
||||||
'td' => array('Cell', $node, $element, $styles, null, null, null),
|
'td' => array('Cell', $node, $element, $styles, null, null, null),
|
||||||
|
|
@ -506,6 +515,9 @@ class Html
|
||||||
case 'text-align':
|
case 'text-align':
|
||||||
$styles['alignment'] = self::mapAlign($cValue);
|
$styles['alignment'] = self::mapAlign($cValue);
|
||||||
break;
|
break;
|
||||||
|
case 'display':
|
||||||
|
$styles['hidden'] = $cValue === 'none' || $cValue === 'hidden';
|
||||||
|
break;
|
||||||
case 'direction':
|
case 'direction':
|
||||||
$styles['rtl'] = $cValue === 'rtl';
|
$styles['rtl'] = $cValue === 'rtl';
|
||||||
break;
|
break;
|
||||||
|
|
@ -523,18 +535,27 @@ class Html
|
||||||
$styles['bgColor'] = trim($cValue, '#');
|
$styles['bgColor'] = trim($cValue, '#');
|
||||||
break;
|
break;
|
||||||
case 'line-height':
|
case 'line-height':
|
||||||
if (preg_match('/([0-9]+[a-z]+)/', $cValue, $matches)) {
|
$matches = array();
|
||||||
|
if (preg_match('/([0-9]+\.?[0-9]*[a-z]+)/', $cValue, $matches)) {
|
||||||
|
//matches number with a unit, e.g. 12px, 15pt, 20mm, ...
|
||||||
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT;
|
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT;
|
||||||
$spacing = Converter::cssToTwip($matches[1]) / \PhpOffice\PhpWord\Style\Paragraph::LINE_HEIGHT;
|
$spacing = Converter::cssToTwip($matches[1]);
|
||||||
} elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
|
} elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
|
||||||
|
//matches percentages
|
||||||
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
|
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
|
||||||
$spacing = ((int) $matches[1]) / 100;
|
//we are subtracting 1 line height because the Spacing writer is adding one line
|
||||||
|
$spacing = ((((int) $matches[1]) / 100) * Paragraph::LINE_HEIGHT) - Paragraph::LINE_HEIGHT;
|
||||||
} else {
|
} else {
|
||||||
|
//any other, wich is a multiplier. E.g. 1.2
|
||||||
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
|
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
|
||||||
$spacing = $cValue;
|
//we are subtracting 1 line height because the Spacing writer is adding one line
|
||||||
|
$spacing = ($cValue * Paragraph::LINE_HEIGHT) - Paragraph::LINE_HEIGHT;
|
||||||
}
|
}
|
||||||
$styles['spacingLineRule'] = $spacingLineRule;
|
$styles['spacingLineRule'] = $spacingLineRule;
|
||||||
$styles['lineHeight'] = $spacing;
|
$styles['line-spacing'] = $spacing;
|
||||||
|
break;
|
||||||
|
case 'letter-spacing':
|
||||||
|
$styles['letter-spacing'] = Converter::cssToTwip($cValue);
|
||||||
break;
|
break;
|
||||||
case 'text-indent':
|
case 'text-indent':
|
||||||
$styles['indentation']['firstLine'] = Converter::cssToTwip($cValue);
|
$styles['indentation']['firstLine'] = Converter::cssToTwip($cValue);
|
||||||
|
|
@ -560,7 +581,7 @@ class Html
|
||||||
$styles['spaceAfter'] = Converter::cssToPoint($cValue);
|
$styles['spaceAfter'] = Converter::cssToPoint($cValue);
|
||||||
break;
|
break;
|
||||||
case 'border-color':
|
case 'border-color':
|
||||||
$styles['color'] = trim($cValue, '#');
|
self::mapBorderColor($styles, $cValue);
|
||||||
break;
|
break;
|
||||||
case 'border-width':
|
case 'border-width':
|
||||||
$styles['borderSize'] = Converter::cssToPoint($cValue);
|
$styles['borderSize'] = Converter::cssToPoint($cValue);
|
||||||
|
|
@ -648,7 +669,52 @@ class Html
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$newElement = $element->addImage($src, $style);
|
$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;
|
return $newElement;
|
||||||
}
|
}
|
||||||
|
|
@ -672,6 +738,20 @@ class Html
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function mapBorderColor(&$styles, $cssBorderColor)
|
||||||
|
{
|
||||||
|
$numColors = substr_count($cssBorderColor, '#');
|
||||||
|
if ($numColors === 1) {
|
||||||
|
$styles['borderColor'] = trim($cssBorderColor, '#');
|
||||||
|
} elseif ($numColors > 1) {
|
||||||
|
$colors = explode(' ', $cssBorderColor);
|
||||||
|
$borders = array('borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor');
|
||||||
|
for ($i = 0; $i < min(4, $numColors, count($colors)); $i++) {
|
||||||
|
$styles[$borders[$i]] = trim($colors[$i], '#');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a HTML/CSS alignment into a \PhpOffice\PhpWord\SimpleType\Jc
|
* Transforms a HTML/CSS alignment into a \PhpOffice\PhpWord\SimpleType\Jc
|
||||||
*
|
*
|
||||||
|
|
@ -690,8 +770,6 @@ class Html
|
||||||
default:
|
default:
|
||||||
return Jc::START;
|
return Jc::START;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of PHPWord - A pure PHP library for reading and writing
|
|
||||||
* word processing documents.
|
|
||||||
*
|
|
||||||
* PHPWord is free software distributed under the terms of the GNU Lesser
|
|
||||||
* General Public License version 3 as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please read the LICENSE
|
|
||||||
* file that was distributed with this source code. For the full list of
|
|
||||||
* contributors, visit https://github.com/PHPOffice/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\Microsoft;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Password encoder for microsoft office applications
|
|
||||||
*/
|
|
||||||
class PasswordEncoder
|
|
||||||
{
|
|
||||||
const ALGORITHM_MD2 = 'MD2';
|
|
||||||
const ALGORITHM_MD4 = 'MD4';
|
|
||||||
const ALGORITHM_MD5 = 'MD5';
|
|
||||||
const ALGORITHM_SHA_1 = 'SHA-1';
|
|
||||||
const ALGORITHM_SHA_256 = 'SHA-256';
|
|
||||||
const ALGORITHM_SHA_384 = 'SHA-384';
|
|
||||||
const ALGORITHM_SHA_512 = 'SHA-512';
|
|
||||||
const ALGORITHM_RIPEMD = 'RIPEMD';
|
|
||||||
const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
|
|
||||||
const ALGORITHM_MAC = 'MAC';
|
|
||||||
const ALGORITHM_HMAC = 'HMAC';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mapping between algorithm name and algorithm ID
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
* @see https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.writeprotection.cryptographicalgorithmsid(v=office.14).aspx
|
|
||||||
*/
|
|
||||||
private static $algorithmMapping = array(
|
|
||||||
self::ALGORITHM_MD2 => array(1, 'md2'),
|
|
||||||
self::ALGORITHM_MD4 => array(2, 'md4'),
|
|
||||||
self::ALGORITHM_MD5 => array(3, 'md5'),
|
|
||||||
self::ALGORITHM_SHA_1 => array(4, 'sha1'),
|
|
||||||
self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash()
|
|
||||||
self::ALGORITHM_RIPEMD => array(6, 'ripemd'),
|
|
||||||
self::ALGORITHM_RIPEMD_160 => array(7, 'ripemd160'),
|
|
||||||
self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash()
|
|
||||||
self::ALGORITHM_SHA_256 => array(12, 'sha256'),
|
|
||||||
self::ALGORITHM_SHA_384 => array(13, 'sha384'),
|
|
||||||
self::ALGORITHM_SHA_512 => array(14, 'sha512'),
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $initialCodeArray = array(
|
|
||||||
0xE1F0,
|
|
||||||
0x1D0F,
|
|
||||||
0xCC9C,
|
|
||||||
0x84C0,
|
|
||||||
0x110C,
|
|
||||||
0x0E10,
|
|
||||||
0xF1CE,
|
|
||||||
0x313E,
|
|
||||||
0x1872,
|
|
||||||
0xE139,
|
|
||||||
0xD40F,
|
|
||||||
0x84F9,
|
|
||||||
0x280C,
|
|
||||||
0xA96A,
|
|
||||||
0x4EC3,
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $encryptionMatrix = array(
|
|
||||||
array(0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09),
|
|
||||||
array(0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF),
|
|
||||||
array(0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0),
|
|
||||||
array(0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40),
|
|
||||||
array(0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5),
|
|
||||||
array(0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A),
|
|
||||||
array(0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9),
|
|
||||||
array(0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0),
|
|
||||||
array(0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC),
|
|
||||||
array(0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10),
|
|
||||||
array(0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168),
|
|
||||||
array(0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C),
|
|
||||||
array(0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD),
|
|
||||||
array(0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC),
|
|
||||||
array(0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4),
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $passwordMaxLength = 15;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a hashed password that MS Word will be able to work with
|
|
||||||
* @see https://blogs.msdn.microsoft.com/vsod/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0/
|
|
||||||
*
|
|
||||||
* @param string $password
|
|
||||||
* @param string $algorithmName
|
|
||||||
* @param string $salt
|
|
||||||
* @param int $spinCount
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function hashPassword($password, $algorithmName = self::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000)
|
|
||||||
{
|
|
||||||
$origEncoding = mb_internal_encoding();
|
|
||||||
mb_internal_encoding('UTF-8');
|
|
||||||
|
|
||||||
$password = mb_substr($password, 0, min(self::$passwordMaxLength, mb_strlen($password)));
|
|
||||||
|
|
||||||
// Get the single-byte values by iterating through the Unicode characters of the truncated password.
|
|
||||||
// For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte.
|
|
||||||
$passUtf8 = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
|
|
||||||
$byteChars = array();
|
|
||||||
|
|
||||||
for ($i = 0; $i < mb_strlen($password); $i++) {
|
|
||||||
$byteChars[$i] = ord(substr($passUtf8, $i * 2, 1));
|
|
||||||
|
|
||||||
if ($byteChars[$i] == 0) {
|
|
||||||
$byteChars[$i] = ord(substr($passUtf8, $i * 2 + 1, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// build low-order word and hig-order word and combine them
|
|
||||||
$combinedKey = self::buildCombinedKey($byteChars);
|
|
||||||
// build reversed hexadecimal string
|
|
||||||
$hex = str_pad(strtoupper(dechex($combinedKey & 0xFFFFFFFF)), 8, '0', \STR_PAD_LEFT);
|
|
||||||
$reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1];
|
|
||||||
|
|
||||||
$generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8');
|
|
||||||
|
|
||||||
// Implementation Notes List:
|
|
||||||
// Word requires that the initial hash of the password with the salt not be considered in the count.
|
|
||||||
// The initial hash of salt + key is not included in the iteration count.
|
|
||||||
$algorithm = self::getAlgorithm($algorithmName);
|
|
||||||
$generatedKey = hash($algorithm, $salt . $generatedKey, true);
|
|
||||||
|
|
||||||
for ($i = 0; $i < $spinCount; $i++) {
|
|
||||||
$generatedKey = hash($algorithm, $generatedKey . pack('CCCC', $i, $i >> 8, $i >> 16, $i >> 24), true);
|
|
||||||
}
|
|
||||||
$generatedKey = base64_encode($generatedKey);
|
|
||||||
|
|
||||||
mb_internal_encoding($origEncoding);
|
|
||||||
|
|
||||||
return $generatedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get algorithm from self::$algorithmMapping
|
|
||||||
*
|
|
||||||
* @param string $algorithmName
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private static function getAlgorithm($algorithmName)
|
|
||||||
{
|
|
||||||
$algorithm = self::$algorithmMapping[$algorithmName][1];
|
|
||||||
if ($algorithm == '') {
|
|
||||||
$algorithm = 'sha1';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the algorithm ID
|
|
||||||
*
|
|
||||||
* @param string $algorithmName
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function getAlgorithmId($algorithmName)
|
|
||||||
{
|
|
||||||
return self::$algorithmMapping[$algorithmName][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build combined key from low-order word and high-order word
|
|
||||||
*
|
|
||||||
* @param array $byteChars byte array representation of password
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private static function buildCombinedKey($byteChars)
|
|
||||||
{
|
|
||||||
$byteCharsLength = count($byteChars);
|
|
||||||
// Compute the high-order word
|
|
||||||
// Initialize from the initial code array (see above), depending on the passwords length.
|
|
||||||
$highOrderWord = self::$initialCodeArray[$byteCharsLength - 1];
|
|
||||||
|
|
||||||
// For each character in the password:
|
|
||||||
// For every bit in the character, starting with the least significant and progressing to (but excluding)
|
|
||||||
// the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from
|
|
||||||
// the Encryption Matrix
|
|
||||||
for ($i = 0; $i < $byteCharsLength; $i++) {
|
|
||||||
$tmp = self::$passwordMaxLength - $byteCharsLength + $i;
|
|
||||||
$matrixRow = self::$encryptionMatrix[$tmp];
|
|
||||||
for ($intBit = 0; $intBit < 7; $intBit++) {
|
|
||||||
if (($byteChars[$i] & (0x0001 << $intBit)) != 0) {
|
|
||||||
$highOrderWord = ($highOrderWord ^ $matrixRow[$intBit]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute low-order word
|
|
||||||
// Initialize with 0
|
|
||||||
$lowOrderWord = 0;
|
|
||||||
// For each character in the password, going backwards
|
|
||||||
for ($i = $byteCharsLength - 1; $i >= 0; $i--) {
|
|
||||||
// low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character
|
|
||||||
$lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteChars[$i]);
|
|
||||||
}
|
|
||||||
// Lastly, low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B.
|
|
||||||
$lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteCharsLength ^ 0xCE4B);
|
|
||||||
|
|
||||||
// Combine the Low and High Order Word
|
|
||||||
return self::int32(($highOrderWord << 16) + $lowOrderWord);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulate behaviour of (signed) int32
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @param int $value
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private static function int32($value)
|
|
||||||
{
|
|
||||||
$value = ($value & 0xFFFFFFFF);
|
|
||||||
|
|
||||||
if ($value & 0x80000000) {
|
|
||||||
$value = -((~$value & 0xFFFFFFFF) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -129,6 +129,7 @@ class ZipArchive
|
||||||
{
|
{
|
||||||
$result = true;
|
$result = true;
|
||||||
$this->filename = $filename;
|
$this->filename = $filename;
|
||||||
|
$this->tempDir = Settings::getTempDir();
|
||||||
|
|
||||||
if (!$this->usePclzip) {
|
if (!$this->usePclzip) {
|
||||||
$zip = new \ZipArchive();
|
$zip = new \ZipArchive();
|
||||||
|
|
@ -139,7 +140,6 @@ class ZipArchive
|
||||||
$this->numFiles = $zip->numFiles;
|
$this->numFiles = $zip->numFiles;
|
||||||
} else {
|
} else {
|
||||||
$zip = new \PclZip($this->filename);
|
$zip = new \PclZip($this->filename);
|
||||||
$this->tempDir = Settings::getTempDir();
|
|
||||||
$zipContent = $zip->listContent();
|
$zipContent = $zip->listContent();
|
||||||
$this->numFiles = is_array($zipContent) ? count($zipContent) : 0;
|
$this->numFiles = is_array($zipContent) ? count($zipContent) : 0;
|
||||||
}
|
}
|
||||||
|
|
@ -245,14 +245,20 @@ class ZipArchive
|
||||||
$pathRemoved = $filenameParts['dirname'];
|
$pathRemoved = $filenameParts['dirname'];
|
||||||
$pathAdded = $localnameParts['dirname'];
|
$pathAdded = $localnameParts['dirname'];
|
||||||
|
|
||||||
$res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded);
|
if (!$this->usePclzip) {
|
||||||
|
$pathAdded = $pathAdded . '/' . ltrim(str_replace('\\', '/', substr($filename, strlen($pathRemoved))), '/');
|
||||||
|
//$res = $zip->addFile($filename, $pathAdded);
|
||||||
|
$res = $zip->addFromString($pathAdded, file_get_contents($filename)); // addFile can't use subfolders in some cases
|
||||||
|
} else {
|
||||||
|
$res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded);
|
||||||
|
}
|
||||||
|
|
||||||
if ($tempFile) {
|
if ($tempFile) {
|
||||||
// Remove temp file, if created
|
// Remove temp file, if created
|
||||||
unlink($this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']);
|
unlink($this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($res == 0) ? false : true;
|
return $res != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -283,7 +289,7 @@ class ZipArchive
|
||||||
// Remove temp file
|
// Remove temp file
|
||||||
@unlink($this->tempDir . DIRECTORY_SEPARATOR . $filenameParts['basename']);
|
@unlink($this->tempDir . DIRECTORY_SEPARATOR . $filenameParts['basename']);
|
||||||
|
|
||||||
return ($res == 0) ? false : true;
|
return $res != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -303,7 +309,7 @@ class ZipArchive
|
||||||
if (is_null($entries)) {
|
if (is_null($entries)) {
|
||||||
$result = $zip->extract(PCLZIP_OPT_PATH, $destination);
|
$result = $zip->extract(PCLZIP_OPT_PATH, $destination);
|
||||||
|
|
||||||
return ($result > 0) ? true : false;
|
return $result > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract by entries
|
// Extract by entries
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of PHPWord - A pure PHP library for reading and writing
|
||||||
|
* word processing documents.
|
||||||
|
*
|
||||||
|
* PHPWord is free software distributed under the terms of the GNU Lesser
|
||||||
|
* General Public License version 3 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the LICENSE
|
||||||
|
* file that was distributed with this source code. For the full list of
|
||||||
|
* contributors, visit https://github.com/PHPOffice/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 PhpOffice\PhpWord\Shared\AbstractEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical Alignment Type.
|
||||||
|
*
|
||||||
|
* Introduced in ISO/IEC-29500:2008.
|
||||||
|
*
|
||||||
|
* @see http://www.datypic.com/sc/ooxml/t-w_ST_VerticalJc.html
|
||||||
|
* @since 0.17.0
|
||||||
|
*/
|
||||||
|
final class VerticalJc extends AbstractEnum
|
||||||
|
{
|
||||||
|
const TOP = 'top';
|
||||||
|
const CENTER = 'center';
|
||||||
|
const BOTH = 'both';
|
||||||
|
const BOTTOM = 'bottom';
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ class Style
|
||||||
* Add paragraph style
|
* Add paragraph style
|
||||||
*
|
*
|
||||||
* @param string $styleName
|
* @param string $styleName
|
||||||
* @param array $styles
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $styles
|
||||||
* @return \PhpOffice\PhpWord\Style\Paragraph
|
* @return \PhpOffice\PhpWord\Style\Paragraph
|
||||||
*/
|
*/
|
||||||
public static function addParagraphStyle($styleName, $styles)
|
public static function addParagraphStyle($styleName, $styles)
|
||||||
|
|
@ -51,8 +51,8 @@ class Style
|
||||||
* Add font style
|
* Add font style
|
||||||
*
|
*
|
||||||
* @param string $styleName
|
* @param string $styleName
|
||||||
* @param array $fontStyle
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $fontStyle
|
||||||
* @param array $paragraphStyle
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $paragraphStyle
|
||||||
* @return \PhpOffice\PhpWord\Style\Font
|
* @return \PhpOffice\PhpWord\Style\Font
|
||||||
*/
|
*/
|
||||||
public static function addFontStyle($styleName, $fontStyle, $paragraphStyle = null)
|
public static function addFontStyle($styleName, $fontStyle, $paragraphStyle = null)
|
||||||
|
|
@ -64,7 +64,7 @@ class Style
|
||||||
* Add link style
|
* Add link style
|
||||||
*
|
*
|
||||||
* @param string $styleName
|
* @param string $styleName
|
||||||
* @param array $styles
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $styles
|
||||||
* @return \PhpOffice\PhpWord\Style\Font
|
* @return \PhpOffice\PhpWord\Style\Font
|
||||||
*/
|
*/
|
||||||
public static function addLinkStyle($styleName, $styles)
|
public static function addLinkStyle($styleName, $styles)
|
||||||
|
|
@ -76,7 +76,7 @@ class Style
|
||||||
* Add numbering style
|
* Add numbering style
|
||||||
*
|
*
|
||||||
* @param string $styleName
|
* @param string $styleName
|
||||||
* @param array $styleValues
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $styleValues
|
||||||
* @return \PhpOffice\PhpWord\Style\Numbering
|
* @return \PhpOffice\PhpWord\Style\Numbering
|
||||||
* @since 0.10.0
|
* @since 0.10.0
|
||||||
*/
|
*/
|
||||||
|
|
@ -88,14 +88,14 @@ class Style
|
||||||
/**
|
/**
|
||||||
* Add title style
|
* Add title style
|
||||||
*
|
*
|
||||||
* @param int $depth
|
* @param int|null $depth Provide null to set title font
|
||||||
* @param array $fontStyle
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $fontStyle
|
||||||
* @param array $paragraphStyle
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $paragraphStyle
|
||||||
* @return \PhpOffice\PhpWord\Style\Font
|
* @return \PhpOffice\PhpWord\Style\Font
|
||||||
*/
|
*/
|
||||||
public static function addTitleStyle($depth, $fontStyle, $paragraphStyle = null)
|
public static function addTitleStyle($depth, $fontStyle, $paragraphStyle = null)
|
||||||
{
|
{
|
||||||
if ($depth == null) {
|
if (empty($depth)) {
|
||||||
$styleName = 'Title';
|
$styleName = 'Title';
|
||||||
} else {
|
} else {
|
||||||
$styleName = "Heading_{$depth}";
|
$styleName = "Heading_{$depth}";
|
||||||
|
|
@ -141,7 +141,7 @@ class Style
|
||||||
/**
|
/**
|
||||||
* Set default paragraph style
|
* Set default paragraph style
|
||||||
*
|
*
|
||||||
* @param array $styles Paragraph style definition
|
* @param array|\PhpOffice\PhpWord\Style\AbstractStyle $styles Paragraph style definition
|
||||||
* @return \PhpOffice\PhpWord\Style\Paragraph
|
* @return \PhpOffice\PhpWord\Style\Paragraph
|
||||||
*/
|
*/
|
||||||
public static function setDefaultParagraphStyle($styles)
|
public static function setDefaultParagraphStyle($styles)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
namespace PhpOffice\PhpWord\Style;
|
namespace PhpOffice\PhpWord\Style;
|
||||||
|
|
||||||
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
||||||
|
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table cell style
|
* Table cell style
|
||||||
|
|
@ -28,10 +29,20 @@ class Cell extends Border
|
||||||
* Vertical alignment constants
|
* Vertical alignment constants
|
||||||
*
|
*
|
||||||
* @const string
|
* @const string
|
||||||
|
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::TOP instead
|
||||||
*/
|
*/
|
||||||
const VALIGN_TOP = 'top';
|
const VALIGN_TOP = 'top';
|
||||||
|
/**
|
||||||
|
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::CENTER instead
|
||||||
|
*/
|
||||||
const VALIGN_CENTER = 'center';
|
const VALIGN_CENTER = 'center';
|
||||||
|
/**
|
||||||
|
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::BOTTOM instead
|
||||||
|
*/
|
||||||
const VALIGN_BOTTOM = 'bottom';
|
const VALIGN_BOTTOM = 'bottom';
|
||||||
|
/**
|
||||||
|
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::BOTH instead
|
||||||
|
*/
|
||||||
const VALIGN_BOTH = 'both';
|
const VALIGN_BOTH = 'both';
|
||||||
|
|
||||||
//Text direction constants
|
//Text direction constants
|
||||||
|
|
@ -145,8 +156,8 @@ class Cell extends Border
|
||||||
*/
|
*/
|
||||||
public function setVAlign($value = null)
|
public function setVAlign($value = null)
|
||||||
{
|
{
|
||||||
$enum = array(self::VALIGN_TOP, self::VALIGN_CENTER, self::VALIGN_BOTTOM, self::VALIGN_BOTH);
|
VerticalJc::validate($value);
|
||||||
$this->vAlign = $this->setEnumVal($value, $enum, $this->vAlign);
|
$this->vAlign = $this->setEnumVal($value, VerticalJc::values(), $this->vAlign);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,20 @@ class Chart extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
private $colors = 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
|
* A list of display options for data labels
|
||||||
*
|
*
|
||||||
|
|
@ -97,9 +111,15 @@ class Chart extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
private $valueAxisTitle;
|
private $valueAxisTitle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The position for major tick marks
|
||||||
|
* Possible values are 'in', 'out', 'cross', 'none'
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
private $majorTickMarkPos = 'none';
|
private $majorTickMarkPos = 'none';
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Show labels for axis
|
* Show labels for axis
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
|
|
@ -221,6 +241,50 @@ class Chart extends AbstractStyle
|
||||||
return $this;
|
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
|
* Show labels for axis
|
||||||
*
|
*
|
||||||
|
|
@ -394,8 +458,8 @@ class Chart extends AbstractStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the position for major tick marks
|
* Set the position for major tick marks
|
||||||
* @param string $position [description]
|
* @param string $position
|
||||||
*/
|
*/
|
||||||
public function setMajorTickPosition($position)
|
public function setMajorTickPosition($position)
|
||||||
{
|
{
|
||||||
|
|
@ -403,7 +467,7 @@ class Chart extends AbstractStyle
|
||||||
$this->majorTickMarkPos = $this->setEnumVal($position, $enum, $this->majorTickMarkPos);
|
$this->majorTickMarkPos = $this->setEnumVal($position, $enum, $this->majorTickMarkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Show Gridlines for X-Axis
|
* Show Gridlines for X-Axis
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class Font extends AbstractStyle
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $aliases = array('line-height' => 'lineHeight');
|
protected $aliases = array('line-height' => 'lineHeight', 'letter-spacing' => 'spacing');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font style type
|
* Font style type
|
||||||
|
|
@ -122,14 +122,14 @@ class Font extends AbstractStyle
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $bold = false;
|
private $bold;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Italic
|
* Italic
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $italic = false;
|
private $italic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undeline
|
* Undeline
|
||||||
|
|
@ -157,14 +157,14 @@ class Font extends AbstractStyle
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $strikethrough = false;
|
private $strikethrough;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Double strikethrough
|
* Double strikethrough
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $doubleStrikethrough = false;
|
private $doubleStrikethrough;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Small caps
|
* Small caps
|
||||||
|
|
@ -172,7 +172,7 @@ class Font extends AbstractStyle
|
||||||
* @var bool
|
* @var bool
|
||||||
* @see 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;
|
private $smallCaps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All caps
|
* All caps
|
||||||
|
|
@ -180,7 +180,7 @@ class Font extends AbstractStyle
|
||||||
* @var bool
|
* @var bool
|
||||||
* @see 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;
|
private $allCaps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Foreground/highlight
|
* Foreground/highlight
|
||||||
|
|
@ -235,7 +235,7 @@ class Font extends AbstractStyle
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $rtl = false;
|
private $rtl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* noProof (disables AutoCorrect)
|
* noProof (disables AutoCorrect)
|
||||||
|
|
@ -243,7 +243,7 @@ class Font extends AbstractStyle
|
||||||
* @var bool
|
* @var bool
|
||||||
* http://www.datypic.com/sc/ooxml/e-w_noProof-1.html
|
* http://www.datypic.com/sc/ooxml/e-w_noProof-1.html
|
||||||
*/
|
*/
|
||||||
private $noProof = false;
|
private $noProof;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Languages
|
* Languages
|
||||||
|
|
@ -252,6 +252,14 @@ class Font extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
private $lang;
|
private $lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden text
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
* @see http://www.datypic.com/sc/ooxml/e-w_vanish-1.html
|
||||||
|
*/
|
||||||
|
private $hidden;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vertically Raised or Lowered Text
|
* Vertically Raised or Lowered Text
|
||||||
*
|
*
|
||||||
|
|
@ -264,7 +272,7 @@ class Font extends AbstractStyle
|
||||||
* Create new font style
|
* Create new font style
|
||||||
*
|
*
|
||||||
* @param string $type Type of font
|
* @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)
|
public function __construct($type = 'text', $paragraph = null)
|
||||||
{
|
{
|
||||||
|
|
@ -299,6 +307,7 @@ class Font extends AbstractStyle
|
||||||
'smallCaps' => $this->isSmallCaps(),
|
'smallCaps' => $this->isSmallCaps(),
|
||||||
'allCaps' => $this->isAllCaps(),
|
'allCaps' => $this->isAllCaps(),
|
||||||
'fgColor' => $this->getFgColor(),
|
'fgColor' => $this->getFgColor(),
|
||||||
|
'hidden' => $this->isHidden(),
|
||||||
),
|
),
|
||||||
'spacing' => array(
|
'spacing' => array(
|
||||||
'scale' => $this->getScale(),
|
'scale' => $this->getScale(),
|
||||||
|
|
@ -938,6 +947,29 @@ class Font extends AbstractStyle
|
||||||
return $this->getParagraph();
|
return $this->getParagraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get hidden text
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isHidden()
|
||||||
|
{
|
||||||
|
return $this->hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set hidden text
|
||||||
|
*
|
||||||
|
* @param bool $value
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setHidden($value = true)
|
||||||
|
{
|
||||||
|
$this->hidden = $this->setBoolVal($value, $this->hidden);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get position
|
* Get position
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,9 @@ final class Language extends AbstractStyle
|
||||||
const HE_IL = 'he-IL';
|
const HE_IL = 'he-IL';
|
||||||
const HE_IL_ID = 1037;
|
const HE_IL_ID = 1037;
|
||||||
|
|
||||||
|
const IT_IT = 'it-IT';
|
||||||
|
const IT_IT_ID = 1040;
|
||||||
|
|
||||||
const JA_JP = 'ja-JP';
|
const JA_JP = 'ja-JP';
|
||||||
const JA_JP_ID = 1041;
|
const JA_JP_ID = 1041;
|
||||||
|
|
||||||
|
|
@ -62,6 +65,12 @@ final class Language extends AbstractStyle
|
||||||
const PT_BR = 'pt-BR';
|
const PT_BR = 'pt-BR';
|
||||||
const PT_BR_ID = 1046;
|
const PT_BR_ID = 1046;
|
||||||
|
|
||||||
|
const NL_NL = 'nl-NL';
|
||||||
|
const NL_NL_ID = 1043;
|
||||||
|
|
||||||
|
const UK_UA = 'uk-UA';
|
||||||
|
const UK_UA_ID = 1058;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language ID, used for RTF document generation
|
* Language ID, used for RTF document generation
|
||||||
*
|
*
|
||||||
|
|
@ -120,8 +129,7 @@ final class Language extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
public function setLatin($latin)
|
public function setLatin($latin)
|
||||||
{
|
{
|
||||||
$this->validateLocale($latin);
|
$this->latin = $this->validateLocale($latin);
|
||||||
$this->latin = $latin;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
@ -170,8 +178,7 @@ final class Language extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
public function setEastAsia($eastAsia)
|
public function setEastAsia($eastAsia)
|
||||||
{
|
{
|
||||||
$this->validateLocale($eastAsia);
|
$this->eastAsia = $this->validateLocale($eastAsia);
|
||||||
$this->eastAsia = $eastAsia;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
@ -195,8 +202,7 @@ final class Language extends AbstractStyle
|
||||||
*/
|
*/
|
||||||
public function setBidirectional($bidirectional)
|
public function setBidirectional($bidirectional)
|
||||||
{
|
{
|
||||||
$this->validateLocale($bidirectional);
|
$this->bidirectional = $this->validateLocale($bidirectional);
|
||||||
$this->bidirectional = $bidirectional;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
@ -215,12 +221,18 @@ final class Language extends AbstractStyle
|
||||||
* Validates that the language passed is in the format xx-xx
|
* Validates that the language passed is in the format xx-xx
|
||||||
*
|
*
|
||||||
* @param string $locale
|
* @param string $locale
|
||||||
* @return bool
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function validateLocale($locale)
|
private function validateLocale($locale)
|
||||||
{
|
{
|
||||||
if ($locale !== null && strstr($locale, '-') === false) {
|
if (strlen($locale) === 2) {
|
||||||
|
return strtolower($locale) . '-' . strtoupper($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($locale !== null && $locale !== 'zxx' && strstr($locale, '-') === false) {
|
||||||
throw new \InvalidArgumentException($locale . ' is not a valid language code');
|
throw new \InvalidArgumentException($locale . ' is not a valid language code');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $locale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class Paragraph extends Border
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $aliases = array('line-height' => 'lineHeight');
|
protected $aliases = array('line-height' => 'lineHeight', 'line-spacing' => 'spacing');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parent style
|
* Parent style
|
||||||
|
|
@ -199,8 +199,6 @@ class Paragraph extends Border
|
||||||
$key = Text::removeUnderscorePrefix($key);
|
$key = Text::removeUnderscorePrefix($key);
|
||||||
if ('indent' == $key || 'hanging' == $key) {
|
if ('indent' == $key || 'hanging' == $key) {
|
||||||
$value = $value * 720;
|
$value = $value * 720;
|
||||||
} elseif ('spacing' == $key) {
|
|
||||||
$value += 240; // because line height of 1 matches 240 twips
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::setStyleValue($key, $value);
|
return parent::setStyleValue($key, $value);
|
||||||
|
|
@ -479,7 +477,7 @@ class Paragraph extends Border
|
||||||
/**
|
/**
|
||||||
* Get spacing between lines
|
* Get spacing between lines
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int|float
|
||||||
*/
|
*/
|
||||||
public function getSpacing()
|
public function getSpacing()
|
||||||
{
|
{
|
||||||
|
|
@ -489,7 +487,7 @@ class Paragraph extends Border
|
||||||
/**
|
/**
|
||||||
* Set spacing between lines
|
* Set spacing between lines
|
||||||
*
|
*
|
||||||
* @param int $value
|
* @param int|float $value
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setSpacing($value = null)
|
public function setSpacing($value = null)
|
||||||
|
|
@ -547,7 +545,8 @@ class Paragraph extends Border
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->lineHeight = $lineHeight;
|
$this->lineHeight = $lineHeight;
|
||||||
$this->setSpacing($lineHeight * self::LINE_HEIGHT);
|
$this->setSpacing(($lineHeight - 1) * self::LINE_HEIGHT);
|
||||||
|
$this->setSpacingLineRule(\PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Style;
|
namespace PhpOffice\PhpWord\Style;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Section settings
|
* Section settings
|
||||||
*/
|
*/
|
||||||
|
|
@ -166,6 +168,14 @@ class Section extends Border
|
||||||
*/
|
*/
|
||||||
private $lineNumbering;
|
private $lineNumbering;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical Text Alignment on Page
|
||||||
|
* One of \PhpOffice\PhpWord\SimpleType\VerticalJc
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $vAlign;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new instance
|
* Create new instance
|
||||||
*/
|
*/
|
||||||
|
|
@ -599,4 +609,28 @@ class Section extends Border
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get vertical alignment
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getVAlign()
|
||||||
|
{
|
||||||
|
return $this->vAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set vertical alignment
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setVAlign($value = null)
|
||||||
|
{
|
||||||
|
VerticalJc::validate($value);
|
||||||
|
$this->vAlign = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,21 @@ class Table extends Border
|
||||||
/** @var TblWidthComplexType|null */
|
/** @var TblWidthComplexType|null */
|
||||||
private $indent;
|
private $indent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of each column, computed based on the max cell width of each column
|
||||||
|
*
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
private $columnWidths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visually Right to Left Table
|
||||||
|
*
|
||||||
|
* @see http://www.datypic.com/sc/ooxml/e-w_bidiVisual-1.html
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $bidiVisual = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new table style
|
* Create new table style
|
||||||
*
|
*
|
||||||
|
|
@ -748,4 +763,48 @@ class Table extends Border
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the columnWidths
|
||||||
|
*
|
||||||
|
* @return null|int[]
|
||||||
|
*/
|
||||||
|
public function getColumnWidths()
|
||||||
|
{
|
||||||
|
return $this->columnWidths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column widths
|
||||||
|
*
|
||||||
|
* @param int[] $value
|
||||||
|
*/
|
||||||
|
public function setColumnWidths(array $value = null)
|
||||||
|
{
|
||||||
|
$this->columnWidths = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get bidiVisual
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isBidiVisual()
|
||||||
|
{
|
||||||
|
return $this->bidiVisual;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set bidiVisual
|
||||||
|
*
|
||||||
|
* @param bool $bidi
|
||||||
|
* Set to true to visually present table as Right to Left
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setBidiVisual($bidi)
|
||||||
|
{
|
||||||
|
$this->bidiVisual = $bidi;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
namespace PhpOffice\PhpWord;
|
namespace PhpOffice\PhpWord;
|
||||||
|
|
||||||
use PhpOffice\Common\Text;
|
use PhpOffice\Common\Text;
|
||||||
|
use PhpOffice\Common\XMLWriter;
|
||||||
use PhpOffice\PhpWord\Escaper\RegExp;
|
use PhpOffice\PhpWord\Escaper\RegExp;
|
||||||
use PhpOffice\PhpWord\Escaper\Xml;
|
use PhpOffice\PhpWord\Escaper\Xml;
|
||||||
use PhpOffice\PhpWord\Exception\CopyFileException;
|
use PhpOffice\PhpWord\Exception\CopyFileException;
|
||||||
|
|
@ -48,6 +49,13 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected $tempDocumentMainPart;
|
protected $tempDocumentMainPart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content of settings part (in XML format) of the temporary document
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $tempDocumentSettingsPart;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content of headers (in XML format) of the temporary document
|
* Content of headers (in XML format) of the temporary document
|
||||||
*
|
*
|
||||||
|
|
@ -62,6 +70,27 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected $tempDocumentFooters = array();
|
protected $tempDocumentFooters = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document relations (in XML format) of the temporary document.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $tempDocumentRelations = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document content types (in XML format) of the temporary document.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $tempDocumentContentTypes = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* new inserted images list
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $tempDocumentNewImages = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception
|
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception
|
||||||
*
|
*
|
||||||
|
|
@ -75,12 +104,12 @@ class TemplateProcessor
|
||||||
// Temporary document filename initialization
|
// Temporary document filename initialization
|
||||||
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||||
if (false === $this->tempDocumentFilename) {
|
if (false === $this->tempDocumentFilename) {
|
||||||
throw new CreateTemporaryFileException();
|
throw new CreateTemporaryFileException(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template file cloning
|
// Template file cloning
|
||||||
if (false === copy($documentTemplate, $this->tempDocumentFilename)) {
|
if (false === copy($documentTemplate, $this->tempDocumentFilename)) {
|
||||||
throw new CopyFileException($documentTemplate, $this->tempDocumentFilename);
|
throw new CopyFileException($documentTemplate, $this->tempDocumentFilename); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary document content extraction
|
// Temporary document content extraction
|
||||||
|
|
@ -88,19 +117,47 @@ class TemplateProcessor
|
||||||
$this->zipClass->open($this->tempDocumentFilename);
|
$this->zipClass->open($this->tempDocumentFilename);
|
||||||
$index = 1;
|
$index = 1;
|
||||||
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
|
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
|
||||||
$this->tempDocumentHeaders[$index] = $this->fixBrokenMacros(
|
$this->tempDocumentHeaders[$index] = $this->readPartWithRels($this->getHeaderName($index));
|
||||||
$this->zipClass->getFromName($this->getHeaderName($index))
|
|
||||||
);
|
|
||||||
$index++;
|
$index++;
|
||||||
}
|
}
|
||||||
$index = 1;
|
$index = 1;
|
||||||
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
|
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
|
||||||
$this->tempDocumentFooters[$index] = $this->fixBrokenMacros(
|
$this->tempDocumentFooters[$index] = $this->readPartWithRels($this->getFooterName($index));
|
||||||
$this->zipClass->getFromName($this->getFooterName($index))
|
|
||||||
);
|
|
||||||
$index++;
|
$index++;
|
||||||
}
|
}
|
||||||
$this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName($this->getMainPartName()));
|
|
||||||
|
$this->tempDocumentMainPart = $this->readPartWithRels($this->getMainPartName());
|
||||||
|
$this->tempDocumentSettingsPart = $this->readPartWithRels($this->getSettingsPartName());
|
||||||
|
$this->tempDocumentContentTypes = $this->zipClass->getFromName($this->getDocumentContentTypesName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose zip class
|
||||||
|
*
|
||||||
|
* To replace an image: $templateProcessor->zip()->AddFromString("word/media/image1.jpg", file_get_contents($file));<br>
|
||||||
|
* To read a file: $templateProcessor->zip()->getFromName("word/media/image1.jpg");
|
||||||
|
*
|
||||||
|
* @return \PhpOffice\PhpWord\Shared\ZipArchive
|
||||||
|
*/
|
||||||
|
public function zip()
|
||||||
|
{
|
||||||
|
return $this->zipClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $fileName
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function readPartWithRels($fileName)
|
||||||
|
{
|
||||||
|
$relsFileName = $this->getRelationsName($fileName);
|
||||||
|
$partRelations = $this->zipClass->getFromName($relsFileName);
|
||||||
|
if ($partRelations !== false) {
|
||||||
|
$this->tempDocumentRelations[$fileName] = $partRelations;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fixBrokenMacros($this->zipClass->getFromName($fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,6 +170,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected function transformSingleXml($xml, $xsltProcessor)
|
protected function transformSingleXml($xml, $xsltProcessor)
|
||||||
{
|
{
|
||||||
|
libxml_disable_entity_loader(true);
|
||||||
$domDocument = new \DOMDocument();
|
$domDocument = new \DOMDocument();
|
||||||
if (false === $domDocument->loadXML($xml)) {
|
if (false === $domDocument->loadXML($xml)) {
|
||||||
throw new Exception('Could not load the given XML document.');
|
throw new Exception('Could not load the given XML document.');
|
||||||
|
|
@ -138,6 +196,7 @@ class TemplateProcessor
|
||||||
foreach ($xml as &$item) {
|
foreach ($xml as &$item) {
|
||||||
$item = $this->transformSingleXml($item, $xsltProcessor);
|
$item = $this->transformSingleXml($item, $xsltProcessor);
|
||||||
}
|
}
|
||||||
|
unset($item);
|
||||||
} else {
|
} else {
|
||||||
$xml = $this->transformSingleXml($xml, $xsltProcessor);
|
$xml = $this->transformSingleXml($xml, $xsltProcessor);
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +258,46 @@ class TemplateProcessor
|
||||||
return $subject;
|
return $subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $search
|
||||||
|
* @param \PhpOffice\PhpWord\Element\AbstractElement $complexType
|
||||||
|
*/
|
||||||
|
public function setComplexValue($search, \PhpOffice\PhpWord\Element\AbstractElement $complexType)
|
||||||
|
{
|
||||||
|
$elementName = substr(get_class($complexType), strrpos(get_class($complexType), '\\') + 1);
|
||||||
|
$objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName;
|
||||||
|
|
||||||
|
$xmlWriter = new XMLWriter();
|
||||||
|
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */
|
||||||
|
$elementWriter = new $objectClass($xmlWriter, $complexType, true);
|
||||||
|
$elementWriter->write();
|
||||||
|
|
||||||
|
$where = $this->findContainingXmlBlockForMacro($search, 'w:r');
|
||||||
|
$block = $this->getSlice($where['start'], $where['end']);
|
||||||
|
$textParts = $this->splitTextIntoTexts($block);
|
||||||
|
$this->replaceXmlBlock($search, $textParts, 'w:r');
|
||||||
|
|
||||||
|
$search = static::ensureMacroCompleted($search);
|
||||||
|
$this->replaceXmlBlock($search, $xmlWriter->getData(), 'w:r');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $search
|
||||||
|
* @param \PhpOffice\PhpWord\Element\AbstractElement $complexType
|
||||||
|
*/
|
||||||
|
public function setComplexBlock($search, \PhpOffice\PhpWord\Element\AbstractElement $complexType)
|
||||||
|
{
|
||||||
|
$elementName = substr(get_class($complexType), strrpos(get_class($complexType), '\\') + 1);
|
||||||
|
$objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName;
|
||||||
|
|
||||||
|
$xmlWriter = new XMLWriter();
|
||||||
|
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */
|
||||||
|
$elementWriter = new $objectClass($xmlWriter, $complexType, false);
|
||||||
|
$elementWriter->write();
|
||||||
|
|
||||||
|
$this->replaceXmlBlock($search, $xmlWriter->getData(), 'w:p');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $search
|
* @param mixed $search
|
||||||
* @param mixed $replace
|
* @param mixed $replace
|
||||||
|
|
@ -208,18 +307,20 @@ class TemplateProcessor
|
||||||
{
|
{
|
||||||
if (is_array($search)) {
|
if (is_array($search)) {
|
||||||
foreach ($search as &$item) {
|
foreach ($search as &$item) {
|
||||||
$item = self::ensureMacroCompleted($item);
|
$item = static::ensureMacroCompleted($item);
|
||||||
}
|
}
|
||||||
|
unset($item);
|
||||||
} else {
|
} else {
|
||||||
$search = self::ensureMacroCompleted($search);
|
$search = static::ensureMacroCompleted($search);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($replace)) {
|
if (is_array($replace)) {
|
||||||
foreach ($replace as &$item) {
|
foreach ($replace as &$item) {
|
||||||
$item = self::ensureUtf8Encoded($item);
|
$item = static::ensureUtf8Encoded($item);
|
||||||
}
|
}
|
||||||
|
unset($item);
|
||||||
} else {
|
} else {
|
||||||
$replace = self::ensureUtf8Encoded($replace);
|
$replace = static::ensureUtf8Encoded($replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::isOutputEscapingEnabled()) {
|
if (Settings::isOutputEscapingEnabled()) {
|
||||||
|
|
@ -232,6 +333,312 @@ class TemplateProcessor
|
||||||
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
|
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set values from a one-dimensional array of "variable => value"-pairs.
|
||||||
|
*
|
||||||
|
* @param array $values
|
||||||
|
*/
|
||||||
|
public function setValues(array $values)
|
||||||
|
{
|
||||||
|
foreach ($values as $macro => $replace) {
|
||||||
|
$this->setValue($macro, $replace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getImageArgs($varNameWithArgs)
|
||||||
|
{
|
||||||
|
$varElements = explode(':', $varNameWithArgs);
|
||||||
|
array_shift($varElements); // first element is name of variable => remove it
|
||||||
|
|
||||||
|
$varInlineArgs = array();
|
||||||
|
// size format documentation: https://msdn.microsoft.com/en-us/library/documentformat.openxml.vml.shape%28v=office.14%29.aspx?f=255&MSPPError=-2147217396
|
||||||
|
foreach ($varElements as $argIdx => $varArg) {
|
||||||
|
if (strpos($varArg, '=')) { // arg=value
|
||||||
|
list($argName, $argValue) = explode('=', $varArg, 2);
|
||||||
|
$argName = strtolower($argName);
|
||||||
|
if ($argName == 'size') {
|
||||||
|
list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $argValue, 2);
|
||||||
|
} else {
|
||||||
|
$varInlineArgs[strtolower($argName)] = $argValue;
|
||||||
|
}
|
||||||
|
} elseif (preg_match('/^([0-9]*[a-z%]{0,2}|auto)x([0-9]*[a-z%]{0,2}|auto)$/i', $varArg)) { // 60x40
|
||||||
|
list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $varArg, 2);
|
||||||
|
} else { // :60:40:f
|
||||||
|
switch ($argIdx) {
|
||||||
|
case 0:
|
||||||
|
$varInlineArgs['width'] = $varArg;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$varInlineArgs['height'] = $varArg;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$varInlineArgs['ratio'] = $varArg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $varInlineArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function chooseImageDimension($baseValue, $inlineValue, $defaultValue)
|
||||||
|
{
|
||||||
|
$value = $baseValue;
|
||||||
|
if (is_null($value) && isset($inlineValue)) {
|
||||||
|
$value = $inlineValue;
|
||||||
|
}
|
||||||
|
if (!preg_match('/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i', $value)) {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = $defaultValue;
|
||||||
|
}
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
$value .= 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fixImageWidthHeightRatio(&$width, &$height, $actualWidth, $actualHeight)
|
||||||
|
{
|
||||||
|
$imageRatio = $actualWidth / $actualHeight;
|
||||||
|
|
||||||
|
if (($width === '') && ($height === '')) { // defined size are empty
|
||||||
|
$width = $actualWidth . 'px';
|
||||||
|
$height = $actualHeight . 'px';
|
||||||
|
} elseif ($width === '') { // defined width is empty
|
||||||
|
$heightFloat = (float) $height;
|
||||||
|
$widthFloat = $heightFloat * $imageRatio;
|
||||||
|
$matches = array();
|
||||||
|
preg_match("/\d([a-z%]+)$/", $height, $matches);
|
||||||
|
$width = $widthFloat . $matches[1];
|
||||||
|
} elseif ($height === '') { // defined height is empty
|
||||||
|
$widthFloat = (float) $width;
|
||||||
|
$heightFloat = $widthFloat / $imageRatio;
|
||||||
|
$matches = array();
|
||||||
|
preg_match("/\d([a-z%]+)$/", $width, $matches);
|
||||||
|
$height = $heightFloat . $matches[1];
|
||||||
|
} else { // we have defined size, but we need also check it aspect ratio
|
||||||
|
$widthMatches = array();
|
||||||
|
preg_match("/\d([a-z%]+)$/", $width, $widthMatches);
|
||||||
|
$heightMatches = array();
|
||||||
|
preg_match("/\d([a-z%]+)$/", $height, $heightMatches);
|
||||||
|
// try to fix only if dimensions are same
|
||||||
|
if ($widthMatches[1] == $heightMatches[1]) {
|
||||||
|
$dimention = $widthMatches[1];
|
||||||
|
$widthFloat = (float) $width;
|
||||||
|
$heightFloat = (float) $height;
|
||||||
|
$definedRatio = $widthFloat / $heightFloat;
|
||||||
|
|
||||||
|
if ($imageRatio > $definedRatio) { // image wider than defined box
|
||||||
|
$height = ($widthFloat / $imageRatio) . $dimention;
|
||||||
|
} elseif ($imageRatio < $definedRatio) { // image higher than defined box
|
||||||
|
$width = ($heightFloat * $imageRatio) . $dimention;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepareImageAttrs($replaceImage, $varInlineArgs)
|
||||||
|
{
|
||||||
|
// get image path and size
|
||||||
|
$width = null;
|
||||||
|
$height = null;
|
||||||
|
$ratio = null;
|
||||||
|
if (is_array($replaceImage) && isset($replaceImage['path'])) {
|
||||||
|
$imgPath = $replaceImage['path'];
|
||||||
|
if (isset($replaceImage['width'])) {
|
||||||
|
$width = $replaceImage['width'];
|
||||||
|
}
|
||||||
|
if (isset($replaceImage['height'])) {
|
||||||
|
$height = $replaceImage['height'];
|
||||||
|
}
|
||||||
|
if (isset($replaceImage['ratio'])) {
|
||||||
|
$ratio = $replaceImage['ratio'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$imgPath = $replaceImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
$width = $this->chooseImageDimension($width, isset($varInlineArgs['width']) ? $varInlineArgs['width'] : null, 115);
|
||||||
|
$height = $this->chooseImageDimension($height, isset($varInlineArgs['height']) ? $varInlineArgs['height'] : null, 70);
|
||||||
|
|
||||||
|
$imageData = @getimagesize($imgPath);
|
||||||
|
if (!is_array($imageData)) {
|
||||||
|
throw new Exception(sprintf('Invalid image: %s', $imgPath));
|
||||||
|
}
|
||||||
|
list($actualWidth, $actualHeight, $imageType) = $imageData;
|
||||||
|
|
||||||
|
// fix aspect ratio (by default)
|
||||||
|
if (is_null($ratio) && isset($varInlineArgs['ratio'])) {
|
||||||
|
$ratio = $varInlineArgs['ratio'];
|
||||||
|
}
|
||||||
|
if (is_null($ratio) || !in_array(strtolower($ratio), array('', '-', 'f', 'false'))) {
|
||||||
|
$this->fixImageWidthHeightRatio($width, $height, $actualWidth, $actualHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
$imageAttrs = array(
|
||||||
|
'src' => $imgPath,
|
||||||
|
'mime' => image_type_to_mime_type($imageType),
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $imageAttrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addImageToRelations($partFileName, $rid, $imgPath, $imageMimeType)
|
||||||
|
{
|
||||||
|
// define templates
|
||||||
|
$typeTpl = '<Override PartName="/word/media/{IMG}" ContentType="image/{EXT}"/>';
|
||||||
|
$relationTpl = '<Relationship Id="{RID}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/{IMG}"/>';
|
||||||
|
$newRelationsTpl = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n" . '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
|
||||||
|
$newRelationsTypeTpl = '<Override PartName="/{RELS}" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
|
||||||
|
$extTransform = array(
|
||||||
|
'image/jpeg' => 'jpeg',
|
||||||
|
'image/png' => 'png',
|
||||||
|
'image/bmp' => 'bmp',
|
||||||
|
'image/gif' => 'gif',
|
||||||
|
);
|
||||||
|
|
||||||
|
// get image embed name
|
||||||
|
if (isset($this->tempDocumentNewImages[$imgPath])) {
|
||||||
|
$imgName = $this->tempDocumentNewImages[$imgPath];
|
||||||
|
} else {
|
||||||
|
// transform extension
|
||||||
|
if (isset($extTransform[$imageMimeType])) {
|
||||||
|
$imgExt = $extTransform[$imageMimeType];
|
||||||
|
} else {
|
||||||
|
throw new Exception("Unsupported image type $imageMimeType");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add image to document
|
||||||
|
$imgName = 'image_' . $rid . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt;
|
||||||
|
$this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName);
|
||||||
|
$this->tempDocumentNewImages[$imgPath] = $imgName;
|
||||||
|
|
||||||
|
// setup type for image
|
||||||
|
$xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl);
|
||||||
|
$this->tempDocumentContentTypes = str_replace('</Types>', $xmlImageType, $this->tempDocumentContentTypes) . '</Types>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl);
|
||||||
|
|
||||||
|
if (!isset($this->tempDocumentRelations[$partFileName])) {
|
||||||
|
// create new relations file
|
||||||
|
$this->tempDocumentRelations[$partFileName] = $newRelationsTpl;
|
||||||
|
// and add it to content types
|
||||||
|
$xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl);
|
||||||
|
$this->tempDocumentContentTypes = str_replace('</Types>', $xmlRelationsType, $this->tempDocumentContentTypes) . '</Types>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// add image to relations
|
||||||
|
$this->tempDocumentRelations[$partFileName] = str_replace('</Relationships>', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . '</Relationships>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $search
|
||||||
|
* @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz)
|
||||||
|
* @param int $limit
|
||||||
|
*/
|
||||||
|
public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
|
||||||
|
{
|
||||||
|
// prepare $search_replace
|
||||||
|
if (!is_array($search)) {
|
||||||
|
$search = array($search);
|
||||||
|
}
|
||||||
|
|
||||||
|
$replacesList = array();
|
||||||
|
if (!is_array($replace) || isset($replace['path'])) {
|
||||||
|
$replacesList[] = $replace;
|
||||||
|
} else {
|
||||||
|
$replacesList = array_values($replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
$searchReplace = array();
|
||||||
|
foreach ($search as $searchIdx => $searchString) {
|
||||||
|
$searchReplace[$searchString] = isset($replacesList[$searchIdx]) ? $replacesList[$searchIdx] : $replacesList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect document parts
|
||||||
|
$searchParts = array(
|
||||||
|
$this->getMainPartName() => &$this->tempDocumentMainPart,
|
||||||
|
);
|
||||||
|
foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) {
|
||||||
|
$searchParts[$this->getHeaderName($headerIndex)] = &$this->tempDocumentHeaders[$headerIndex];
|
||||||
|
}
|
||||||
|
foreach (array_keys($this->tempDocumentFooters) as $headerIndex) {
|
||||||
|
$searchParts[$this->getFooterName($headerIndex)] = &$this->tempDocumentFooters[$headerIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// define templates
|
||||||
|
// result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425)
|
||||||
|
$imgTpl = '<w:pict><v:shape type="#_x0000_t75" style="width:{WIDTH};height:{HEIGHT}"><v:imagedata r:id="{RID}" o:title=""/></v:shape></w:pict>';
|
||||||
|
|
||||||
|
foreach ($searchParts as $partFileName => &$partContent) {
|
||||||
|
$partVariables = $this->getVariablesForPart($partContent);
|
||||||
|
|
||||||
|
foreach ($searchReplace as $searchString => $replaceImage) {
|
||||||
|
$varsToReplace = array_filter($partVariables, function ($partVar) use ($searchString) {
|
||||||
|
return ($partVar == $searchString) || preg_match('/^' . preg_quote($searchString) . ':/', $partVar);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($varsToReplace as $varNameWithArgs) {
|
||||||
|
$varInlineArgs = $this->getImageArgs($varNameWithArgs);
|
||||||
|
$preparedImageAttrs = $this->prepareImageAttrs($replaceImage, $varInlineArgs);
|
||||||
|
$imgPath = $preparedImageAttrs['src'];
|
||||||
|
|
||||||
|
// get image index
|
||||||
|
$imgIndex = $this->getNextRelationsIndex($partFileName);
|
||||||
|
$rid = 'rId' . $imgIndex;
|
||||||
|
|
||||||
|
// replace preparations
|
||||||
|
$this->addImageToRelations($partFileName, $rid, $imgPath, $preparedImageAttrs['mime']);
|
||||||
|
$xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $preparedImageAttrs['width'], $preparedImageAttrs['height']), $imgTpl);
|
||||||
|
|
||||||
|
// replace variable
|
||||||
|
$varNameWithArgsFixed = static::ensureMacroCompleted($varNameWithArgs);
|
||||||
|
$matches = array();
|
||||||
|
if (preg_match('/(<[^<]+>)([^<]*)(' . preg_quote($varNameWithArgsFixed) . ')([^>]*)(<[^>]+>)/Uu', $partContent, $matches)) {
|
||||||
|
$wholeTag = $matches[0];
|
||||||
|
array_shift($matches);
|
||||||
|
list($openTag, $prefix, , $postfix, $closeTag) = $matches;
|
||||||
|
$replaceXml = $openTag . $prefix . $closeTag . $xmlImage . $openTag . $postfix . $closeTag;
|
||||||
|
// replace on each iteration, because in one tag we can have 2+ inline variables => before proceed next variable we need to change $partContent
|
||||||
|
$partContent = $this->setValueForPart($wholeTag, $replaceXml, $partContent, $limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns count of all variables in template.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getVariableCount()
|
||||||
|
{
|
||||||
|
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
|
||||||
|
|
||||||
|
foreach ($this->tempDocumentHeaders as $headerXML) {
|
||||||
|
$variables = array_merge(
|
||||||
|
$variables,
|
||||||
|
$this->getVariablesForPart($headerXML)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->tempDocumentFooters as $footerXML) {
|
||||||
|
$variables = array_merge(
|
||||||
|
$variables,
|
||||||
|
$this->getVariablesForPart($footerXML)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_count_values($variables);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of all variables in template.
|
* Returns array of all variables in template.
|
||||||
*
|
*
|
||||||
|
|
@ -239,17 +646,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
public function getVariables()
|
public function getVariables()
|
||||||
{
|
{
|
||||||
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
|
return array_keys($this->getVariableCount());
|
||||||
|
|
||||||
foreach ($this->tempDocumentHeaders as $headerXML) {
|
|
||||||
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->tempDocumentFooters as $footerXML) {
|
|
||||||
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_unique($variables);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -262,9 +659,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
public function cloneRow($search, $numberOfClones)
|
public function cloneRow($search, $numberOfClones)
|
||||||
{
|
{
|
||||||
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
|
$search = static::ensureMacroCompleted($search);
|
||||||
$search = '${' . $search . '}';
|
|
||||||
}
|
|
||||||
|
|
||||||
$tagPos = strpos($this->tempDocumentMainPart, $search);
|
$tagPos = strpos($this->tempDocumentMainPart, $search);
|
||||||
if (!$tagPos) {
|
if (!$tagPos) {
|
||||||
|
|
@ -291,7 +686,7 @@ class TemplateProcessor
|
||||||
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
|
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
|
||||||
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
|
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
|
||||||
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
|
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
|
||||||
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
|
!preg_match('#<w:vMerge w:val="continue"\s*/>#', $tmpXmlRow)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// This row was a spanned row, update $rowEnd and search for the next row.
|
// This row was a spanned row, update $rowEnd and search for the next row.
|
||||||
|
|
@ -301,37 +696,62 @@ class TemplateProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->getSlice(0, $rowStart);
|
$result = $this->getSlice(0, $rowStart);
|
||||||
for ($i = 1; $i <= $numberOfClones; $i++) {
|
$result .= implode($this->indexClonedVariables($numberOfClones, $xmlRow));
|
||||||
$result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
|
|
||||||
}
|
|
||||||
$result .= $this->getSlice($rowEnd);
|
$result .= $this->getSlice($rowEnd);
|
||||||
|
|
||||||
$this->tempDocumentMainPart = $result;
|
$this->tempDocumentMainPart = $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a table row and populates it's values from a two-dimensional array in a template document.
|
||||||
|
*
|
||||||
|
* @param string $search
|
||||||
|
* @param array $values
|
||||||
|
*/
|
||||||
|
public function cloneRowAndSetValues($search, $values)
|
||||||
|
{
|
||||||
|
$this->cloneRow($search, count($values));
|
||||||
|
|
||||||
|
foreach ($values as $rowKey => $rowData) {
|
||||||
|
$rowNumber = $rowKey + 1;
|
||||||
|
foreach ($rowData as $macro => $replace) {
|
||||||
|
$this->setValue($macro . '#' . $rowNumber, $replace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone a block.
|
* Clone a block.
|
||||||
*
|
*
|
||||||
* @param string $blockname
|
* @param string $blockname
|
||||||
* @param int $clones
|
* @param int $clones How many time the block should be cloned
|
||||||
* @param bool $replace
|
* @param bool $replace
|
||||||
|
* @param bool $indexVariables If true, any variables inside the block will be indexed (postfixed with #1, #2, ...)
|
||||||
|
* @param array $variableReplacements Array containing replacements for macros found inside the block to clone
|
||||||
*
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function cloneBlock($blockname, $clones = 1, $replace = true)
|
public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
|
||||||
{
|
{
|
||||||
$xmlBlock = null;
|
$xmlBlock = null;
|
||||||
|
$matches = array();
|
||||||
preg_match(
|
preg_match(
|
||||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
'/(<\?xml.*)(<w:p\b.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p\b.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
$this->tempDocumentMainPart,
|
$this->tempDocumentMainPart,
|
||||||
$matches
|
$matches
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isset($matches[3])) {
|
if (isset($matches[3])) {
|
||||||
$xmlBlock = $matches[3];
|
$xmlBlock = $matches[3];
|
||||||
$cloned = array();
|
if ($indexVariables) {
|
||||||
for ($i = 1; $i <= $clones; $i++) {
|
$cloned = $this->indexClonedVariables($clones, $xmlBlock);
|
||||||
$cloned[] = $xmlBlock;
|
} elseif ($variableReplacements !== null && is_array($variableReplacements)) {
|
||||||
|
$cloned = $this->replaceClonedVariables($variableReplacements, $xmlBlock);
|
||||||
|
} else {
|
||||||
|
$cloned = array();
|
||||||
|
for ($i = 1; $i <= $clones; $i++) {
|
||||||
|
$cloned[] = $xmlBlock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($replace) {
|
if ($replace) {
|
||||||
|
|
@ -354,6 +774,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
public function replaceBlock($blockname, $replacement)
|
public function replaceBlock($blockname, $replacement)
|
||||||
{
|
{
|
||||||
|
$matches = array();
|
||||||
preg_match(
|
preg_match(
|
||||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
$this->tempDocumentMainPart,
|
$this->tempDocumentMainPart,
|
||||||
|
|
@ -379,6 +800,22 @@ class TemplateProcessor
|
||||||
$this->replaceBlock($blockname, '');
|
$this->replaceBlock($blockname, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically Recalculate Fields on Open
|
||||||
|
*
|
||||||
|
* @param bool $update
|
||||||
|
*/
|
||||||
|
public function setUpdateFields($update = true)
|
||||||
|
{
|
||||||
|
$string = $update ? 'true' : 'false';
|
||||||
|
$matches = array();
|
||||||
|
if (preg_match('/<w:updateFields w:val=\"(true|false|1|0|on|off)\"\/>/', $this->tempDocumentSettingsPart, $matches)) {
|
||||||
|
$this->tempDocumentSettingsPart = str_replace($matches[0], '<w:updateFields w:val="' . $string . '"/>', $this->tempDocumentSettingsPart);
|
||||||
|
} else {
|
||||||
|
$this->tempDocumentSettingsPart = str_replace('</w:settings>', '<w:updateFields w:val="' . $string . '"/></w:settings>', $this->tempDocumentSettingsPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the result document.
|
* Saves the result document.
|
||||||
*
|
*
|
||||||
|
|
@ -389,23 +826,39 @@ class TemplateProcessor
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
foreach ($this->tempDocumentHeaders as $index => $xml) {
|
foreach ($this->tempDocumentHeaders as $index => $xml) {
|
||||||
$this->zipClass->addFromString($this->getHeaderName($index), $xml);
|
$this->savePartWithRels($this->getHeaderName($index), $xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->zipClass->addFromString($this->getMainPartName(), $this->tempDocumentMainPart);
|
$this->savePartWithRels($this->getMainPartName(), $this->tempDocumentMainPart);
|
||||||
|
$this->savePartWithRels($this->getSettingsPartName(), $this->tempDocumentSettingsPart);
|
||||||
|
|
||||||
foreach ($this->tempDocumentFooters as $index => $xml) {
|
foreach ($this->tempDocumentFooters as $index => $xml) {
|
||||||
$this->zipClass->addFromString($this->getFooterName($index), $xml);
|
$this->savePartWithRels($this->getFooterName($index), $xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes);
|
||||||
|
|
||||||
// Close zip file
|
// Close zip file
|
||||||
if (false === $this->zipClass->close()) {
|
if (false === $this->zipClass->close()) {
|
||||||
throw new Exception('Could not close zip file.');
|
throw new Exception('Could not close zip file.'); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->tempDocumentFilename;
|
return $this->tempDocumentFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $fileName
|
||||||
|
* @param string $xml
|
||||||
|
*/
|
||||||
|
protected function savePartWithRels($fileName, $xml)
|
||||||
|
{
|
||||||
|
$this->zipClass->addFromString($fileName, $xml);
|
||||||
|
if (isset($this->tempDocumentRelations[$fileName])) {
|
||||||
|
$relsFileName = $this->getRelationsName($fileName);
|
||||||
|
$this->zipClass->addFromString($relsFileName, $this->tempDocumentRelations[$fileName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the result document to the user defined file.
|
* Saves the result document to the user defined file.
|
||||||
*
|
*
|
||||||
|
|
@ -441,17 +894,13 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected function fixBrokenMacros($documentPart)
|
protected function fixBrokenMacros($documentPart)
|
||||||
{
|
{
|
||||||
$fixedDocumentPart = $documentPart;
|
return preg_replace_callback(
|
||||||
|
'/\$(?:\{|[^{$]*\>\{)[^}$]*\}/U',
|
||||||
$fixedDocumentPart = preg_replace_callback(
|
|
||||||
'|\$[^{]*\{[^}]*\}|U',
|
|
||||||
function ($match) {
|
function ($match) {
|
||||||
return strip_tags($match[0]);
|
return strip_tags($match[0]);
|
||||||
},
|
},
|
||||||
$fixedDocumentPart
|
$documentPart
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fixedDocumentPart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -484,6 +933,7 @@ class TemplateProcessor
|
||||||
*/
|
*/
|
||||||
protected function getVariablesForPart($documentPartXML)
|
protected function getVariablesForPart($documentPartXML)
|
||||||
{
|
{
|
||||||
|
$matches = array();
|
||||||
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
||||||
|
|
||||||
return $matches[1];
|
return $matches[1];
|
||||||
|
|
@ -502,11 +952,30 @@ 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
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getMainPartName()
|
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"~';
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
preg_match($pattern, $contentTypes, $matches);
|
||||||
|
|
||||||
|
return array_key_exists(1, $matches) ? $matches[1] : 'word/document.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the file containing the Settings part
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getSettingsPartName()
|
||||||
|
{
|
||||||
|
return 'word/settings.xml';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -521,6 +990,35 @@ class TemplateProcessor
|
||||||
return sprintf('word/footer%d.xml', $index);
|
return sprintf('word/footer%d.xml', $index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the relations file for document part.
|
||||||
|
*
|
||||||
|
* @param string $documentPartName
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getRelationsName($documentPartName)
|
||||||
|
{
|
||||||
|
return 'word/_rels/' . pathinfo($documentPartName, PATHINFO_BASENAME) . '.rels';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getNextRelationsIndex($documentPartName)
|
||||||
|
{
|
||||||
|
if (isset($this->tempDocumentRelations[$documentPartName])) {
|
||||||
|
return substr_count($this->tempDocumentRelations[$documentPartName], '<Relationship');
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getDocumentContentTypesName()
|
||||||
|
{
|
||||||
|
return '[Content_Types].xml';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the start position of the nearest table row before $offset.
|
* Find the start position of the nearest table row before $offset.
|
||||||
*
|
*
|
||||||
|
|
@ -572,4 +1070,182 @@ class TemplateProcessor
|
||||||
|
|
||||||
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
|
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces variable names in cloned
|
||||||
|
* rows/blocks with indexed names
|
||||||
|
*
|
||||||
|
* @param int $count
|
||||||
|
* @param string $xmlBlock
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function indexClonedVariables($count, $xmlBlock)
|
||||||
|
{
|
||||||
|
$results = array();
|
||||||
|
for ($i = 1; $i <= $count; $i++) {
|
||||||
|
$results[] = preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raplaces variables with values from array, array keys are the variable names
|
||||||
|
*
|
||||||
|
* @param array $variableReplacements
|
||||||
|
* @param string $xmlBlock
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function replaceClonedVariables($variableReplacements, $xmlBlock)
|
||||||
|
{
|
||||||
|
$results = array();
|
||||||
|
foreach ($variableReplacements as $replacementArray) {
|
||||||
|
$localXmlBlock = $xmlBlock;
|
||||||
|
foreach ($replacementArray as $search => $replacement) {
|
||||||
|
$localXmlBlock = $this->setValueForPart(self::ensureMacroCompleted($search), $replacement, $localXmlBlock, self::MAXIMUM_REPLACEMENTS_DEFAULT);
|
||||||
|
}
|
||||||
|
$results[] = $localXmlBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace an XML block surrounding a macro with a new block
|
||||||
|
*
|
||||||
|
* @param string $macro Name of macro
|
||||||
|
* @param string $block New block content
|
||||||
|
* @param string $blockType XML tag type of block
|
||||||
|
* @return \PhpOffice\PhpWord\TemplateProcessor Fluent interface
|
||||||
|
*/
|
||||||
|
protected function replaceXmlBlock($macro, $block, $blockType = 'w:p')
|
||||||
|
{
|
||||||
|
$where = $this->findContainingXmlBlockForMacro($macro, $blockType);
|
||||||
|
if (is_array($where)) {
|
||||||
|
$this->tempDocumentMainPart = $this->getSlice(0, $where['start']) . $block . $this->getSlice($where['end']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find start and end of XML block containing the given macro
|
||||||
|
* e.g. <w:p>...${macro}...</w:p>
|
||||||
|
*
|
||||||
|
* Note that only the first instance of the macro will be found
|
||||||
|
*
|
||||||
|
* @param string $macro Name of macro
|
||||||
|
* @param string $blockType XML tag for block
|
||||||
|
* @return bool|int[] FALSE if not found, otherwise array with start and end
|
||||||
|
*/
|
||||||
|
protected function findContainingXmlBlockForMacro($macro, $blockType = 'w:p')
|
||||||
|
{
|
||||||
|
$macroPos = $this->findMacro($macro);
|
||||||
|
if (0 > $macroPos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$start = $this->findXmlBlockStart($macroPos, $blockType);
|
||||||
|
if (0 > $start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$end = $this->findXmlBlockEnd($start, $blockType);
|
||||||
|
//if not found or if resulting string does not contain the macro we are searching for
|
||||||
|
if (0 > $end || strstr($this->getSlice($start, $end), $macro) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('start' => $start, 'end' => $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the position of (the start of) a macro
|
||||||
|
*
|
||||||
|
* Returns -1 if not found, otherwise position of opening $
|
||||||
|
*
|
||||||
|
* Note that only the first instance of the macro will be found
|
||||||
|
*
|
||||||
|
* @param string $search Macro name
|
||||||
|
* @param int $offset Offset from which to start searching
|
||||||
|
* @return int -1 if macro not found
|
||||||
|
*/
|
||||||
|
protected function findMacro($search, $offset = 0)
|
||||||
|
{
|
||||||
|
$search = static::ensureMacroCompleted($search);
|
||||||
|
$pos = strpos($this->tempDocumentMainPart, $search, $offset);
|
||||||
|
|
||||||
|
return ($pos === false) ? -1 : $pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the start position of the nearest XML block start before $offset
|
||||||
|
*
|
||||||
|
* @param int $offset Search position
|
||||||
|
* @param string $blockType XML Block tag
|
||||||
|
* @return int -1 if block start not found
|
||||||
|
*/
|
||||||
|
protected function findXmlBlockStart($offset, $blockType)
|
||||||
|
{
|
||||||
|
$reverseOffset = (strlen($this->tempDocumentMainPart) - $offset) * -1;
|
||||||
|
// first try XML tag with attributes
|
||||||
|
$blockStart = strrpos($this->tempDocumentMainPart, '<' . $blockType . ' ', $reverseOffset);
|
||||||
|
// if not found, or if found but contains the XML tag without attribute
|
||||||
|
if (false === $blockStart || strrpos($this->getSlice($blockStart, $offset), '<' . $blockType . '>')) {
|
||||||
|
// also try XML tag without attributes
|
||||||
|
$blockStart = strrpos($this->tempDocumentMainPart, '<' . $blockType . '>', $reverseOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($blockStart === false) ? -1 : $blockStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the nearest block end position after $offset
|
||||||
|
*
|
||||||
|
* @param int $offset Search position
|
||||||
|
* @param string $blockType XML Block tag
|
||||||
|
* @return int -1 if block end not found
|
||||||
|
*/
|
||||||
|
protected function findXmlBlockEnd($offset, $blockType)
|
||||||
|
{
|
||||||
|
$blockEndStart = strpos($this->tempDocumentMainPart, '</' . $blockType . '>', $offset);
|
||||||
|
// return position of end of tag if found, otherwise -1
|
||||||
|
|
||||||
|
return ($blockEndStart === false) ? -1 : $blockEndStart + 3 + strlen($blockType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a w:r/w:t into a list of w:r where each ${macro} is in a separate w:r
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function splitTextIntoTexts($text)
|
||||||
|
{
|
||||||
|
if (!$this->textNeedsSplitting($text)) {
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
$matches = array();
|
||||||
|
if (preg_match('/(<w:rPr.*<\/w:rPr>)/i', $text, $matches)) {
|
||||||
|
$extractedStyle = $matches[0];
|
||||||
|
} else {
|
||||||
|
$extractedStyle = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$unformattedText = preg_replace('/>\s+</', '><', $text);
|
||||||
|
$result = str_replace(array('${', '}'), array('</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">${', '}</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">'), $unformattedText);
|
||||||
|
|
||||||
|
return str_replace(array('<w:r>' . $extractedStyle . '<w:t xml:space="preserve"></w:t></w:r>', '<w:r><w:t xml:space="preserve"></w:t></w:r>', '<w:t>'), array('', '', '<w:t xml:space="preserve">'), $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if string contains a macro that is not in it's own w:r
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function textNeedsSplitting($text)
|
||||||
|
{
|
||||||
|
return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ abstract class AbstractWriter implements WriterInterface
|
||||||
|
|
||||||
// Temporary file
|
// Temporary file
|
||||||
$this->originalFilename = $filename;
|
$this->originalFilename = $filename;
|
||||||
if (strtolower($filename) == 'php://output' || strtolower($filename) == 'php://stdout') {
|
if (strpos(strtolower($filename), 'php://') === 0) {
|
||||||
$filename = tempnam(Settings::getTempDir(), 'PhpWord');
|
$filename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||||
if (false === $filename) {
|
if (false === $filename) {
|
||||||
$filename = $this->originalFilename; // @codeCoverageIgnore
|
$filename = $this->originalFilename; // @codeCoverageIgnore
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@ class Table extends AbstractElement
|
||||||
$rows = $this->element->getRows();
|
$rows = $this->element->getRows();
|
||||||
$rowCount = count($rows);
|
$rowCount = count($rows);
|
||||||
if ($rowCount > 0) {
|
if ($rowCount > 0) {
|
||||||
$content .= '<table>' . PHP_EOL;
|
$content .= '<table' . self::getTableStyle($this->element->getStyle()) . '>' . PHP_EOL;
|
||||||
|
|
||||||
for ($i = 0; $i < $rowCount; $i++) {
|
for ($i = 0; $i < $rowCount; $i++) {
|
||||||
/** @var $row \PhpOffice\PhpWord\Element\Row Type hint */
|
/** @var $row \PhpOffice\PhpWord\Element\Row Type hint */
|
||||||
$rowStyle = $rows[$i]->getStyle();
|
$rowStyle = $rows[$i]->getStyle();
|
||||||
|
|
@ -112,4 +113,29 @@ class Table extends AbstractElement
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates Table style in CSS equivalent
|
||||||
|
*
|
||||||
|
* @param string|\PhpOffice\PhpWord\Style\Table|null $tableStyle
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getTableStyle($tableStyle = null)
|
||||||
|
{
|
||||||
|
if ($tableStyle == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (is_string($tableStyle)) {
|
||||||
|
$style = ' class="' . $tableStyle;
|
||||||
|
} else {
|
||||||
|
$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 . '"';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class Title extends AbstractElement
|
||||||
$text = $this->escaper->escapeHtml($text);
|
$text = $this->escaper->escapeHtml($text);
|
||||||
}
|
}
|
||||||
} elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) {
|
} elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) {
|
||||||
$writer = new Container($this->parentWriter, $this->element);
|
$writer = new Container($this->parentWriter, $text);
|
||||||
$text = $writer->write();
|
$text = $writer->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ class Font extends AbstractStyle
|
||||||
$css['text-decoration'] .= $this->getValueIf($lineThrough, 'line-through ');
|
$css['text-decoration'] .= $this->getValueIf($lineThrough, 'line-through ');
|
||||||
$css['text-transform'] = $this->getValueIf($style->isAllCaps(), 'uppercase');
|
$css['text-transform'] = $this->getValueIf($style->isAllCaps(), 'uppercase');
|
||||||
$css['font-variant'] = $this->getValueIf($style->isSmallCaps(), 'small-caps');
|
$css['font-variant'] = $this->getValueIf($style->isSmallCaps(), 'small-caps');
|
||||||
|
$css['display'] = $this->getValueIf($style->isHidden(), 'none');
|
||||||
|
|
||||||
$spacing = $style->getSpacing();
|
$spacing = $style->getSpacing();
|
||||||
$css['letter-spacing'] = $this->getValueIf(!is_null($spacing), ($spacing / 20) . 'pt');
|
$css['letter-spacing'] = $this->getValueIf(!is_null($spacing), ($spacing / 20) . 'pt');
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Writer\ODText\Element;
|
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
|
* Table element writer
|
||||||
*
|
*
|
||||||
|
|
@ -36,32 +40,59 @@ class Table extends AbstractElement
|
||||||
}
|
}
|
||||||
$rows = $element->getRows();
|
$rows = $element->getRows();
|
||||||
$rowCount = count($rows);
|
$rowCount = count($rows);
|
||||||
$colCount = $element->countColumns();
|
|
||||||
|
|
||||||
if ($rowCount > 0) {
|
if ($rowCount > 0) {
|
||||||
$xmlWriter->startElement('table:table');
|
$xmlWriter->startElement('table:table');
|
||||||
$xmlWriter->writeAttribute('table:name', $element->getElementId());
|
$xmlWriter->writeAttribute('table:name', $element->getElementId());
|
||||||
$xmlWriter->writeAttribute('table:style', $element->getElementId());
|
$xmlWriter->writeAttribute('table:style', $element->getElementId());
|
||||||
|
|
||||||
$xmlWriter->startElement('table:table-column');
|
// Write columns
|
||||||
$xmlWriter->writeAttribute('table:number-columns-repeated', $colCount);
|
$this->writeColumns($xmlWriter, $element);
|
||||||
$xmlWriter->endElement(); // table:table-column
|
|
||||||
|
|
||||||
|
// Write rows
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
$xmlWriter->startElement('table:table-row');
|
$this->writeRow($xmlWriter, $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
|
|
||||||
}
|
}
|
||||||
$xmlWriter->endElement(); // table:table
|
$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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,7 @@ class Content extends AbstractPart
|
||||||
$style->setStyleName('fr' . $element->getMediaIndex());
|
$style->setStyleName('fr' . $element->getMediaIndex());
|
||||||
$this->autoStyles['Image'][] = $style;
|
$this->autoStyles['Image'][] = $style;
|
||||||
} elseif ($element instanceof Table) {
|
} elseif ($element instanceof Table) {
|
||||||
|
/** @var \PhpOffice\PhpWord\Style\Table $style */
|
||||||
$style = $element->getStyle();
|
$style = $element->getStyle();
|
||||||
if ($style === null) {
|
if ($style === null) {
|
||||||
$style = new TableStyle();
|
$style = new TableStyle();
|
||||||
|
|
@ -246,6 +247,7 @@ class Content extends AbstractPart
|
||||||
$style = Style::getStyle($style);
|
$style = Style::getStyle($style);
|
||||||
}
|
}
|
||||||
$style->setStyleName($element->getElementId());
|
$style->setStyleName($element->getElementId());
|
||||||
|
$style->setColumnWidths($element->findFirstDefinedCellWidths());
|
||||||
$this->autoStyles['Table'][] = $style;
|
$this->autoStyles['Table'][] = $style;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@ class Font extends AbstractStyle
|
||||||
$xmlWriter->writeAttributeIf($style->isSmallCaps(), 'fo:font-variant', 'small-caps');
|
$xmlWriter->writeAttributeIf($style->isSmallCaps(), 'fo:font-variant', 'small-caps');
|
||||||
$xmlWriter->writeAttributeIf($style->isAllCaps(), 'fo:text-transform', 'uppercase');
|
$xmlWriter->writeAttributeIf($style->isAllCaps(), 'fo:text-transform', 'uppercase');
|
||||||
|
|
||||||
|
//Hidden text
|
||||||
|
$xmlWriter->writeAttributeIf($style->isHidden(), 'text:display', 'none');
|
||||||
|
|
||||||
// Superscript/subscript
|
// Superscript/subscript
|
||||||
$xmlWriter->writeAttributeIf($style->isSuperScript(), 'style:text-position', 'super');
|
$xmlWriter->writeAttributeIf($style->isSuperScript(), 'style:text-position', 'super');
|
||||||
$xmlWriter->writeAttributeIf($style->isSubScript(), 'style:text-position', 'sub');
|
$xmlWriter->writeAttributeIf($style->isSubScript(), 'style:text-position', 'sub');
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ class Paragraph extends AbstractStyle
|
||||||
$xmlWriter->writeAttribute('fo:margin-bottom', $marginBottom . 'cm');
|
$xmlWriter->writeAttribute('fo:margin-bottom', $marginBottom . 'cm');
|
||||||
$xmlWriter->writeAttribute('fo:text-align', $style->getAlignment());
|
$xmlWriter->writeAttribute('fo:text-align', $style->getAlignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Right to left
|
||||||
|
$xmlWriter->writeAttributeIf($style->isBidi(), 'style:writing-mode', 'rl-tb');
|
||||||
|
|
||||||
$xmlWriter->endElement(); //style:paragraph-properties
|
$xmlWriter->endElement(); //style:paragraph-properties
|
||||||
|
|
||||||
$xmlWriter->endElement(); //style:style
|
$xmlWriter->endElement(); //style:style
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,22 @@ class Table extends AbstractStyle
|
||||||
//$xmlWriter->writeAttribute('style:width', 'table');
|
//$xmlWriter->writeAttribute('style:width', 'table');
|
||||||
$xmlWriter->writeAttribute('style:rel-width', 100);
|
$xmlWriter->writeAttribute('style:rel-width', 100);
|
||||||
$xmlWriter->writeAttribute('table:align', 'center');
|
$xmlWriter->writeAttribute('table:align', 'center');
|
||||||
|
$xmlWriter->writeAttributeIf($style->isBidiVisual(), 'style:writing-mode', 'rl-tb');
|
||||||
$xmlWriter->endElement(); // style:table-properties
|
$xmlWriter->endElement(); // style:table-properties
|
||||||
$xmlWriter->endElement(); // style:style
|
$xmlWriter->endElement(); // style:style
|
||||||
|
|
||||||
|
$cellWidths = $style->getColumnWidths();
|
||||||
|
$countCellWidths = $cellWidths === null ? 0 : 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class Border extends AbstractStyle
|
||||||
|
|
||||||
$content .= '\pgbrdr' . substr($side, 0, 1);
|
$content .= '\pgbrdr' . substr($side, 0, 1);
|
||||||
$content .= '\brdrs'; // Single-thickness border; @todo Get other type of border
|
$content .= '\brdrs'; // Single-thickness border; @todo Get other type of border
|
||||||
$content .= '\brdrw' . $width; // Width
|
$content .= '\brdrw' . round($width); // Width
|
||||||
$content .= '\brdrcf' . $colorIndex; // Color
|
$content .= '\brdrcf' . $colorIndex; // Color
|
||||||
$content .= '\brsp480'; // Space in twips between borders and the paragraph (24pt, following OOXML)
|
$content .= '\brsp480'; // Space in twips between borders and the paragraph (24pt, following OOXML)
|
||||||
$content .= ' ';
|
$content .= ' ';
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class Font extends AbstractStyle
|
||||||
$content .= '\f' . $this->nameIndex;
|
$content .= '\f' . $this->nameIndex;
|
||||||
|
|
||||||
$size = $style->getSize();
|
$size = $style->getSize();
|
||||||
$content .= $this->getValueIf(is_numeric($size), '\fs' . ($size * 2));
|
$content .= $this->getValueIf(is_numeric($size), '\fs' . round($size * 2));
|
||||||
|
|
||||||
$content .= $this->getValueIf($style->isBold(), '\b');
|
$content .= $this->getValueIf($style->isBold(), '\b');
|
||||||
$content .= $this->getValueIf($style->isItalic(), '\i');
|
$content .= $this->getValueIf($style->isItalic(), '\i');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of PHPWord - A pure PHP library for reading and writing
|
||||||
|
* word processing documents.
|
||||||
|
*
|
||||||
|
* PHPWord is free software distributed under the terms of the GNU Lesser
|
||||||
|
* General Public License version 3 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the LICENSE
|
||||||
|
* file that was distributed with this source code. For the full list of
|
||||||
|
* contributors, visit https://github.com/PHPOffice/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\Style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RTF indentation style writer
|
||||||
|
*
|
||||||
|
* @since 0.11.0
|
||||||
|
*/
|
||||||
|
class Indentation extends AbstractStyle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Write style
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
$style = $this->getStyle();
|
||||||
|
if (!$style instanceof \PhpOffice\PhpWord\Style\Indentation) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = '\fi' . round($style->getFirstLine());
|
||||||
|
$content .= '\li' . round($style->getLeft());
|
||||||
|
$content .= '\ri' . round($style->getRight());
|
||||||
|
|
||||||
|
return $content . ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -64,8 +64,48 @@ class Paragraph extends AbstractStyle
|
||||||
if (isset($alignments[$style->getAlignment()])) {
|
if (isset($alignments[$style->getAlignment()])) {
|
||||||
$content .= $alignments[$style->getAlignment()];
|
$content .= $alignments[$style->getAlignment()];
|
||||||
}
|
}
|
||||||
$content .= $this->getValueIf($spaceBefore !== null, '\sb' . $spaceBefore);
|
$content .= $this->writeIndentation($style->getIndentation());
|
||||||
$content .= $this->getValueIf($spaceAfter !== null, '\sa' . $spaceAfter);
|
$content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore));
|
||||||
|
$content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($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;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,16 +43,16 @@ class Section extends AbstractStyle
|
||||||
$content .= '\sectd ';
|
$content .= '\sectd ';
|
||||||
|
|
||||||
// Size & margin
|
// Size & margin
|
||||||
$content .= $this->getValueIf($style->getPageSizeW() !== null, '\pgwsxn' . $style->getPageSizeW());
|
$content .= $this->getValueIf($style->getPageSizeW() !== null, '\pgwsxn' . round($style->getPageSizeW()));
|
||||||
$content .= $this->getValueIf($style->getPageSizeH() !== null, '\pghsxn' . $style->getPageSizeH());
|
$content .= $this->getValueIf($style->getPageSizeH() !== null, '\pghsxn' . round($style->getPageSizeH()));
|
||||||
$content .= ' ';
|
$content .= ' ';
|
||||||
$content .= $this->getValueIf($style->getMarginTop() !== null, '\margtsxn' . $style->getMarginTop());
|
$content .= $this->getValueIf($style->getMarginTop() !== null, '\margtsxn' . round($style->getMarginTop()));
|
||||||
$content .= $this->getValueIf($style->getMarginRight() !== null, '\margrsxn' . $style->getMarginRight());
|
$content .= $this->getValueIf($style->getMarginRight() !== null, '\margrsxn' . round($style->getMarginRight()));
|
||||||
$content .= $this->getValueIf($style->getMarginBottom() !== null, '\margbsxn' . $style->getMarginBottom());
|
$content .= $this->getValueIf($style->getMarginBottom() !== null, '\margbsxn' . round($style->getMarginBottom()));
|
||||||
$content .= $this->getValueIf($style->getMarginLeft() !== null, '\marglsxn' . $style->getMarginLeft());
|
$content .= $this->getValueIf($style->getMarginLeft() !== null, '\marglsxn' . round($style->getMarginLeft()));
|
||||||
$content .= $this->getValueIf($style->getHeaderHeight() !== null, '\headery' . $style->getHeaderHeight());
|
$content .= $this->getValueIf($style->getHeaderHeight() !== null, '\headery' . round($style->getHeaderHeight()));
|
||||||
$content .= $this->getValueIf($style->getFooterHeight() !== null, '\footery' . $style->getFooterHeight());
|
$content .= $this->getValueIf($style->getFooterHeight() !== null, '\footery' . round($style->getFooterHeight()));
|
||||||
$content .= $this->getValueIf($style->getGutter() !== null, '\guttersxn' . $style->getGutter());
|
$content .= $this->getValueIf($style->getGutter() !== null, '\guttersxn' . round($style->getGutter()));
|
||||||
$content .= ' ';
|
$content .= ' ';
|
||||||
|
|
||||||
// Borders
|
// Borders
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of PHPWord - A pure PHP library for reading and writing
|
||||||
|
* word processing documents.
|
||||||
|
*
|
||||||
|
* PHPWord is free software distributed under the terms of the GNU Lesser
|
||||||
|
* General Public License version 3 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the LICENSE
|
||||||
|
* file that was distributed with this source code. For the full list of
|
||||||
|
* contributors, visit https://github.com/PHPOffice/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\Style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Line numbering style writer
|
||||||
|
*
|
||||||
|
* @since 0.10.0
|
||||||
|
*/
|
||||||
|
class Tab extends AbstractStyle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Write style.
|
||||||
|
*/
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
$style = $this->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' . round($style->getPosition());
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -103,7 +103,9 @@ class Image extends AbstractElement
|
||||||
$style->setPositioning('absolute');
|
$style->setPositioning('absolute');
|
||||||
$styleWriter = new ImageStyleWriter($xmlWriter, $style);
|
$styleWriter = new ImageStyleWriter($xmlWriter, $style);
|
||||||
|
|
||||||
$xmlWriter->startElement('w:p');
|
if (!$this->withoutP) {
|
||||||
|
$xmlWriter->startElement('w:p');
|
||||||
|
}
|
||||||
$xmlWriter->startElement('w:r');
|
$xmlWriter->startElement('w:r');
|
||||||
$xmlWriter->startElement('w:pict');
|
$xmlWriter->startElement('w:pict');
|
||||||
$xmlWriter->startElement('v:shape');
|
$xmlWriter->startElement('v:shape');
|
||||||
|
|
@ -118,6 +120,8 @@ class Image extends AbstractElement
|
||||||
$xmlWriter->endElement(); // v:shape
|
$xmlWriter->endElement(); // v:shape
|
||||||
$xmlWriter->endElement(); // w:pict
|
$xmlWriter->endElement(); // w:pict
|
||||||
$xmlWriter->endElement(); // w:r
|
$xmlWriter->endElement(); // w:r
|
||||||
$xmlWriter->endElement(); // w:p
|
if (!$this->withoutP) {
|
||||||
|
$xmlWriter->endElement(); // w:p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,18 @@ class SDT extends Text
|
||||||
$this->endElementP(); // w:p
|
$this->endElementP(); // w:p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write text.
|
||||||
|
*
|
||||||
|
* @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtText.html
|
||||||
|
* @param \PhpOffice\Common\XMLWriter $xmlWriter
|
||||||
|
*/
|
||||||
|
private function writePlainText(XMLWriter $xmlWriter)
|
||||||
|
{
|
||||||
|
$xmlWriter->startElement('w:text');
|
||||||
|
$xmlWriter->endElement(); // w:text
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write combo box.
|
* Write combo box.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -76,21 +76,7 @@ class Table extends AbstractElement
|
||||||
*/
|
*/
|
||||||
private function writeColumns(XMLWriter $xmlWriter, TableElement $element)
|
private function writeColumns(XMLWriter $xmlWriter, TableElement $element)
|
||||||
{
|
{
|
||||||
$rows = $element->getRows();
|
$cellWidths = $element->findFirstDefinedCellWidths();
|
||||||
$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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$xmlWriter->startElement('w:tblGrid');
|
$xmlWriter->startElement('w:tblGrid');
|
||||||
foreach ($cellWidths as $width) {
|
foreach ($cellWidths as $width) {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class Title extends AbstractElement
|
||||||
$xmlWriter->endElement();
|
$xmlWriter->endElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$bookmarkRId = null;
|
||||||
if ($element->getDepth() !== 0) {
|
if ($element->getDepth() !== 0) {
|
||||||
$rId = $element->getRelationId();
|
$rId = $element->getRelationId();
|
||||||
$bookmarkRId = $element->getPhpWord()->addBookmark();
|
$bookmarkRId = $element->getPhpWord()->addBookmark();
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,6 @@ class Chart extends AbstractPart
|
||||||
{
|
{
|
||||||
$xmlWriter->startElement('c:chart');
|
$xmlWriter->startElement('c:chart');
|
||||||
|
|
||||||
$xmlWriter->writeElementBlock('c:autoTitleDeleted', 'val', 1);
|
|
||||||
|
|
||||||
$this->writePlotArea($xmlWriter);
|
$this->writePlotArea($xmlWriter);
|
||||||
|
|
||||||
$xmlWriter->endElement(); // c:chart
|
$xmlWriter->endElement(); // c:chart
|
||||||
|
|
@ -131,6 +129,34 @@ class Chart extends AbstractPart
|
||||||
$style = $this->element->getStyle();
|
$style = $this->element->getStyle();
|
||||||
$this->options = $this->types[$type];
|
$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('
|
||||||
|
<a:bodyPr/>
|
||||||
|
<a:lstStyle/>
|
||||||
|
<a:p>
|
||||||
|
<a:pPr>
|
||||||
|
<a:defRPr/></a:pPr><a:r><a:rPr/><a:t>' . $title . '</a:t></a:r>
|
||||||
|
<a:endParaRPr/>
|
||||||
|
</a:p>');
|
||||||
|
$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('<c:legend><c:legendPos val="r"/></c:legend>');
|
||||||
|
}
|
||||||
|
|
||||||
$xmlWriter->startElement('c:plotArea');
|
$xmlWriter->startElement('c:plotArea');
|
||||||
$xmlWriter->writeElement('c:layout');
|
$xmlWriter->writeElement('c:layout');
|
||||||
|
|
||||||
|
|
@ -330,11 +356,11 @@ class Chart extends AbstractPart
|
||||||
$valueAxisTitle = $style->getValueAxisTitle();
|
$valueAxisTitle = $style->getValueAxisTitle();
|
||||||
|
|
||||||
if ($axisType == 'c:catAx') {
|
if ($axisType == 'c:catAx') {
|
||||||
if (isset($categoryAxisTitle)) {
|
if (!is_null($categoryAxisTitle)) {
|
||||||
$this->writeAxisTitle($xmlWriter, $categoryAxisTitle);
|
$this->writeAxisTitle($xmlWriter, $categoryAxisTitle);
|
||||||
}
|
}
|
||||||
} elseif ($axisType == 'c:valAx') {
|
} elseif ($axisType == 'c:valAx') {
|
||||||
if (isset($valueAxisTitle)) {
|
if (!is_null($valueAxisTitle)) {
|
||||||
$this->writeAxisTitle($xmlWriter, $valueAxisTitle);
|
$this->writeAxisTitle($xmlWriter, $valueAxisTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Writer\Word2007\Part;
|
namespace PhpOffice\PhpWord\Writer\Word2007\Part;
|
||||||
|
|
||||||
|
use PhpOffice\Common\Microsoft\PasswordEncoder;
|
||||||
use PhpOffice\PhpWord\ComplexType\ProofState;
|
use PhpOffice\PhpWord\ComplexType\ProofState;
|
||||||
use PhpOffice\PhpWord\ComplexType\TrackChangesView;
|
use PhpOffice\PhpWord\ComplexType\TrackChangesView;
|
||||||
use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder;
|
|
||||||
use PhpOffice\PhpWord\Style\Language;
|
use PhpOffice\PhpWord\Style\Language;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,21 @@ abstract class AbstractStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes boolean as 0 or 1
|
||||||
|
*
|
||||||
|
* @param bool $value
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
protected function writeOnOf($value = null)
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value ? '1' : '0';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assemble style array into style string
|
* Assemble style array into style string
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -107,18 +107,21 @@ class Font extends AbstractStyle
|
||||||
$xmlWriter->writeElementIf($size !== null, 'w:szCs', 'w:val', $size * 2);
|
$xmlWriter->writeElementIf($size !== null, 'w:szCs', 'w:val', $size * 2);
|
||||||
|
|
||||||
// Bold, italic
|
// Bold, italic
|
||||||
$xmlWriter->writeElementIf($style->isBold(), 'w:b');
|
$xmlWriter->writeElementIf($style->isBold() !== null, 'w:b', 'w:val', $this->writeOnOf($style->isBold()));
|
||||||
$xmlWriter->writeElementIf($style->isBold(), 'w:bCs');
|
$xmlWriter->writeElementIf($style->isBold() !== null, 'w:bCs', 'w:val', $this->writeOnOf($style->isBold()));
|
||||||
$xmlWriter->writeElementIf($style->isItalic(), 'w:i');
|
$xmlWriter->writeElementIf($style->isItalic() !== null, 'w:i', 'w:val', $this->writeOnOf($style->isItalic()));
|
||||||
$xmlWriter->writeElementIf($style->isItalic(), 'w:iCs');
|
$xmlWriter->writeElementIf($style->isItalic() !== null, 'w:iCs', 'w:val', $this->writeOnOf($style->isItalic()));
|
||||||
|
|
||||||
// Strikethrough, double strikethrough
|
// Strikethrough, double strikethrough
|
||||||
$xmlWriter->writeElementIf($style->isStrikethrough(), 'w:strike');
|
$xmlWriter->writeElementIf($style->isStrikethrough() !== null, 'w:strike', 'w:val', $this->writeOnOf($style->isStrikethrough()));
|
||||||
$xmlWriter->writeElementIf($style->isDoubleStrikethrough(), 'w:dstrike');
|
$xmlWriter->writeElementIf($style->isDoubleStrikethrough() !== null, 'w:dstrike', 'w:val', $this->writeOnOf($style->isDoubleStrikethrough()));
|
||||||
|
|
||||||
// Small caps, all caps
|
// Small caps, all caps
|
||||||
$xmlWriter->writeElementIf($style->isSmallCaps(), 'w:smallCaps');
|
$xmlWriter->writeElementIf($style->isSmallCaps() !== null, 'w:smallCaps', 'w:val', $this->writeOnOf($style->isSmallCaps()));
|
||||||
$xmlWriter->writeElementIf($style->isAllCaps(), 'w:caps');
|
$xmlWriter->writeElementIf($style->isAllCaps() !== null, 'w:caps', 'w:val', $this->writeOnOf($style->isAllCaps()));
|
||||||
|
|
||||||
|
//Hidden text
|
||||||
|
$xmlWriter->writeElementIf($style->isHidden(), 'w:vanish', 'w:val', $this->writeOnOf($style->isHidden()));
|
||||||
|
|
||||||
// Underline
|
// Underline
|
||||||
$xmlWriter->writeElementIf($style->getUnderline() != 'none', 'w:u', 'w:val', $style->getUnderline());
|
$xmlWriter->writeElementIf($style->getUnderline() != 'none', 'w:u', 'w:val', $style->getUnderline());
|
||||||
|
|
@ -136,7 +139,7 @@ class Font extends AbstractStyle
|
||||||
$xmlWriter->writeElementIf($style->getKerning() !== null, 'w:kern', 'w:val', $style->getKerning() * 2);
|
$xmlWriter->writeElementIf($style->getKerning() !== null, 'w:kern', 'w:val', $style->getKerning() * 2);
|
||||||
|
|
||||||
// noProof
|
// noProof
|
||||||
$xmlWriter->writeElementIf($style->isNoProof() !== false, 'w:noProof');
|
$xmlWriter->writeElementIf($style->isNoProof() !== null, 'w:noProof', $this->writeOnOf($style->isNoProof()));
|
||||||
|
|
||||||
// Background-Color
|
// Background-Color
|
||||||
$shading = $style->getShading();
|
$shading = $style->getShading();
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ class Frame extends AbstractStyle
|
||||||
'hPos' => 'mso-position-horizontal',
|
'hPos' => 'mso-position-horizontal',
|
||||||
'vPos' => 'mso-position-vertical',
|
'vPos' => 'mso-position-vertical',
|
||||||
'hPosRelTo' => 'mso-position-horizontal-relative',
|
'hPosRelTo' => 'mso-position-horizontal-relative',
|
||||||
|
'vPosRelTo' => 'mso-position-vertical-relative',
|
||||||
);
|
);
|
||||||
$posStyles = $this->getStyles($style, $properties);
|
$posStyles = $this->getStyles($style, $properties);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ class Section extends AbstractStyle
|
||||||
$xmlWriter->writeAttribute('w:h', $style->getPageSizeH());
|
$xmlWriter->writeAttribute('w:h', $style->getPageSizeH());
|
||||||
$xmlWriter->endElement(); // w:pgSz
|
$xmlWriter->endElement(); // w:pgSz
|
||||||
|
|
||||||
|
// Vertical alignment
|
||||||
|
$vAlign = $style->getVAlign();
|
||||||
|
$xmlWriter->writeElementIf(!is_null($vAlign), 'w:vAlign', 'w:val', $vAlign);
|
||||||
|
|
||||||
// Margins
|
// Margins
|
||||||
$margins = array(
|
$margins = array(
|
||||||
'w:top' => array('getMarginTop', SectionStyle::DEFAULT_MARGIN),
|
'w:top' => array('getMarginTop', SectionStyle::DEFAULT_MARGIN),
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ class Spacing extends AbstractStyle
|
||||||
$xmlWriter->writeAttributeIf(!is_null($after), 'w:after', $this->convertTwip($after));
|
$xmlWriter->writeAttributeIf(!is_null($after), 'w:after', $this->convertTwip($after));
|
||||||
|
|
||||||
$line = $style->getLine();
|
$line = $style->getLine();
|
||||||
|
//if linerule is auto, the spacing is supposed to include the height of the line itself, which is 240 twips
|
||||||
|
if (null !== $line && 'auto' === $style->getLineRule()) {
|
||||||
|
$line += \PhpOffice\PhpWord\Style\Paragraph::LINE_HEIGHT;
|
||||||
|
}
|
||||||
$xmlWriter->writeAttributeIf(!is_null($line), 'w:line', $line);
|
$xmlWriter->writeAttributeIf(!is_null($line), 'w:line', $line);
|
||||||
|
|
||||||
$xmlWriter->writeAttributeIf(!is_null($line), 'w:lineRule', $style->getLineRule());
|
$xmlWriter->writeAttributeIf(!is_null($line), 'w:lineRule', $style->getLineRule());
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@ class Table extends AbstractStyle
|
||||||
$styleWriter = new TablePosition($xmlWriter, $style->getPosition());
|
$styleWriter = new TablePosition($xmlWriter, $style->getPosition());
|
||||||
$styleWriter->write();
|
$styleWriter->write();
|
||||||
|
|
||||||
|
//Right to left
|
||||||
|
$xmlWriter->writeElementIf($style->isBidiVisual() !== null, 'w:bidiVisual', 'w:val', $this->writeOnOf($style->isBidiVisual()));
|
||||||
|
|
||||||
$this->writeMargin($xmlWriter, $style);
|
$this->writeMargin($xmlWriter, $style);
|
||||||
$this->writeBorder($xmlWriter, $style);
|
$this->writeBorder($xmlWriter, $style);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Element;
|
namespace PhpOffice\PhpWord\Element;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for PhpOffice\PhpWord\Element\Cell
|
* Test class for PhpOffice\PhpWord\Element\Cell
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
*/
|
*/
|
||||||
class CellTest extends \PHPUnit\Framework\TestCase
|
class CellTest extends AbstractWebServerEmbeddedTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* New instance
|
* New instance
|
||||||
|
|
@ -165,7 +167,7 @@ class CellTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testAddImageSectionByUrl()
|
public function testAddImageSectionByUrl()
|
||||||
{
|
{
|
||||||
$oCell = new Cell();
|
$oCell = new Cell();
|
||||||
$element = $oCell->addImage('http://php.net/images/logos/php-med-trans-light.gif');
|
$element = $oCell->addImage(self::getRemoteGifImageUrl());
|
||||||
|
|
||||||
$this->assertCount(1, $oCell->getElements());
|
$this->assertCount(1, $oCell->getElements());
|
||||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Element;
|
namespace PhpOffice\PhpWord\Element;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for PhpOffice\PhpWord\Element\Footer
|
* Test class for PhpOffice\PhpWord\Element\Footer
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
*/
|
*/
|
||||||
class FooterTest extends \PHPUnit\Framework\TestCase
|
class FooterTest extends AbstractWebServerEmbeddedTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* New instance
|
* New instance
|
||||||
|
|
@ -116,7 +118,7 @@ class FooterTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testAddImageByUrl()
|
public function testAddImageByUrl()
|
||||||
{
|
{
|
||||||
$oFooter = new Footer(1);
|
$oFooter = new Footer(1);
|
||||||
$element = $oFooter->addImage('http://php.net/images/logos/php-med-trans-light.gif');
|
$element = $oFooter->addImage(self::getRemoteGifImageUrl());
|
||||||
|
|
||||||
$this->assertCount(1, $oFooter->getElements());
|
$this->assertCount(1, $oFooter->getElements());
|
||||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Element;
|
namespace PhpOffice\PhpWord\Element;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for PhpOffice\PhpWord\Element\Header
|
* Test class for PhpOffice\PhpWord\Element\Header
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
*/
|
*/
|
||||||
class HeaderTest extends \PHPUnit\Framework\TestCase
|
class HeaderTest extends AbstractWebServerEmbeddedTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* New instance
|
* New instance
|
||||||
|
|
@ -125,7 +127,7 @@ class HeaderTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testAddImageByUrl()
|
public function testAddImageByUrl()
|
||||||
{
|
{
|
||||||
$oHeader = new Header(1);
|
$oHeader = new Header(1);
|
||||||
$element = $oHeader->addImage('http://php.net/images/logos/php-med-trans-light.gif');
|
$element = $oHeader->addImage(self::getRemoteGifImageUrl());
|
||||||
|
|
||||||
$this->assertCount(1, $oHeader->getElements());
|
$this->assertCount(1, $oHeader->getElements());
|
||||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpWord\Element;
|
namespace PhpOffice\PhpWord\Element;
|
||||||
|
|
||||||
|
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -24,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc;
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
*/
|
*/
|
||||||
class ImageTest extends \PHPUnit\Framework\TestCase
|
class ImageTest extends AbstractWebServerEmbeddedTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* New instance
|
* New instance
|
||||||
|
|
@ -131,7 +132,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||||
*/
|
*/
|
||||||
public function testUnsupportedImage()
|
public function testUnsupportedImage()
|
||||||
{
|
{
|
||||||
//disable ssl verification, never do this in real application, you should pass the certiciate instead!!!
|
//disable ssl verification, never do this in real application, you should pass the certificiate instead!!!
|
||||||
$arrContextOptions = array(
|
$arrContextOptions = array(
|
||||||
'ssl' => array(
|
'ssl' => array(
|
||||||
'verify_peer' => false,
|
'verify_peer' => false,
|
||||||
|
|
@ -139,7 +140,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
stream_context_set_default($arrContextOptions);
|
stream_context_set_default($arrContextOptions);
|
||||||
$object = new Image('https://samples.libav.org/image-samples/RACECAR.BMP');
|
$object = new Image(self::getRemoteBmpImageUrl());
|
||||||
$object->getSource();
|
$object->getSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,7 +216,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||||
*/
|
*/
|
||||||
public function testConstructFromGd()
|
public function testConstructFromGd()
|
||||||
{
|
{
|
||||||
$source = 'http://php.net/images/logos/php-icon.png';
|
$source = self::getRemoteImageUrl();
|
||||||
|
|
||||||
$image = new Image($source);
|
$image = new Image($source);
|
||||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image);
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image);
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ class SDTTest extends \PHPUnit\Framework\TestCase
|
||||||
*/
|
*/
|
||||||
public function testConstruct()
|
public function testConstruct()
|
||||||
{
|
{
|
||||||
$types = array('comboBox', 'dropDownList', 'date');
|
$types = array('plainText', 'comboBox', 'dropDownList', 'date');
|
||||||
$type = $types[rand(0, 2)];
|
$type = $types[rand(0, 3)];
|
||||||
$value = rand(0, 100);
|
$value = rand(0, 100);
|
||||||
$alias = 'alias';
|
$alias = 'alias';
|
||||||
$tag = 'my_tag';
|
$tag = 'my_tag';
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Element;
|
||||||
|
|
||||||
use PhpOffice\PhpWord\PhpWord;
|
use PhpOffice\PhpWord\PhpWord;
|
||||||
use PhpOffice\PhpWord\Style;
|
use PhpOffice\PhpWord\Style;
|
||||||
|
use PhpOffice\PhpWord\Style\Section as SectionStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \PhpOffice\PhpWord\Element\Section
|
* @covers \PhpOffice\PhpWord\Element\Section
|
||||||
|
|
@ -27,6 +28,27 @@ use PhpOffice\PhpWord\Style;
|
||||||
*/
|
*/
|
||||||
class SectionTest extends \PHPUnit\Framework\TestCase
|
class SectionTest extends \PHPUnit\Framework\TestCase
|
||||||
{
|
{
|
||||||
|
public function testConstructorWithDefaultStyle()
|
||||||
|
{
|
||||||
|
$section = new Section(0);
|
||||||
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Section', $section->getStyle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorWithArrayStyle()
|
||||||
|
{
|
||||||
|
$section = new Section(0, array('orientation' => 'landscape'));
|
||||||
|
$style = $section->getStyle();
|
||||||
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Section', $style);
|
||||||
|
$this->assertEquals('landscape', $style->getOrientation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorWithObjectStyle()
|
||||||
|
{
|
||||||
|
$style = new SectionStyle();
|
||||||
|
$section = new Section(0, $style);
|
||||||
|
$this->assertSame($style, $section->getStyle());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::setStyle
|
* @covers ::setStyle
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,22 @@ class TrackChangeTest extends \PHPUnit\Framework\TestCase
|
||||||
$this->assertEquals($date, $oTrackChange->getDate());
|
$this->assertEquals($date, $oTrackChange->getDate());
|
||||||
$this->assertEquals(TrackChange::INSERTED, $oTrackChange->getChangeType());
|
$this->assertEquals(TrackChange::INSERTED, $oTrackChange->getChangeType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New instance with invalid \DateTime (produced by \DateTime::createFromFormat(...))
|
||||||
|
*/
|
||||||
|
public function testConstructDefaultWithInvalidDate()
|
||||||
|
{
|
||||||
|
$author = 'Test User';
|
||||||
|
$date = false;
|
||||||
|
$oTrackChange = new TrackChange(TrackChange::INSERTED, $author, $date);
|
||||||
|
|
||||||
|
$oText = new Text('dummy text');
|
||||||
|
$oText->setTrackChange($oTrackChange);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TrackChange', $oTrackChange);
|
||||||
|
$this->assertEquals($author, $oTrackChange->getAuthor());
|
||||||
|
$this->assertEquals($date, null);
|
||||||
|
$this->assertEquals(TrackChange::INSERTED, $oTrackChange->getChangeType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ use PhpOffice\PhpWord\Element\Image;
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
*/
|
*/
|
||||||
class MediaTest extends \PHPUnit\Framework\TestCase
|
class MediaTest extends AbstractWebServerEmbeddedTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get section media elements
|
* Get section media elements
|
||||||
|
|
@ -49,7 +49,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||||
{
|
{
|
||||||
$local = __DIR__ . '/_files/images/mars.jpg';
|
$local = __DIR__ . '/_files/images/mars.jpg';
|
||||||
$object = __DIR__ . '/_files/documents/sheet.xls';
|
$object = __DIR__ . '/_files/documents/sheet.xls';
|
||||||
$remote = 'http://php.net/images/logos/php-med-trans-light.gif';
|
$remote = self::getRemoteImageUrl();
|
||||||
Media::addElement('section', 'image', $local, new Image($local));
|
Media::addElement('section', 'image', $local, new Image($local));
|
||||||
Media::addElement('section', 'image', $local, new Image($local));
|
Media::addElement('section', 'image', $local, new Image($local));
|
||||||
Media::addElement('section', 'image', $remote, new Image($local));
|
Media::addElement('section', 'image', $remote, new Image($local));
|
||||||
|
|
@ -77,7 +77,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testAddHeaderMediaElement()
|
public function testAddHeaderMediaElement()
|
||||||
{
|
{
|
||||||
$local = __DIR__ . '/_files/images/mars.jpg';
|
$local = __DIR__ . '/_files/images/mars.jpg';
|
||||||
$remote = 'http://php.net/images/logos/php-med-trans-light.gif';
|
$remote = self::getRemoteImageUrl();
|
||||||
Media::addElement('header1', 'image', $local, new Image($local));
|
Media::addElement('header1', 'image', $local, new Image($local));
|
||||||
Media::addElement('header1', 'image', $local, new Image($local));
|
Media::addElement('header1', 'image', $local, new Image($local));
|
||||||
Media::addElement('header1', 'image', $remote, new Image($remote));
|
Media::addElement('header1', 'image', $remote, new Image($remote));
|
||||||
|
|
@ -92,7 +92,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||||
public function testAddFooterMediaElement()
|
public function testAddFooterMediaElement()
|
||||||
{
|
{
|
||||||
$local = __DIR__ . '/_files/images/mars.jpg';
|
$local = __DIR__ . '/_files/images/mars.jpg';
|
||||||
$remote = 'http://php.net/images/logos/php-med-trans-light.gif';
|
$remote = self::getRemoteImageUrl();
|
||||||
Media::addElement('footer1', 'image', $local, new Image($local));
|
Media::addElement('footer1', 'image', $local, new Image($local));
|
||||||
Media::addElement('footer1', 'image', $local, new Image($local));
|
Media::addElement('footer1', 'image', $local, new Image($local));
|
||||||
Media::addElement('footer1', 'image', $remote, new Image($remote));
|
Media::addElement('footer1', 'image', $remote, new Image($remote));
|
||||||
|
|
|
||||||
|
|
@ -236,4 +236,40 @@ class ElementTest extends AbstractTestReader
|
||||||
$this->assertEquals('Title', $formattedTitle->getStyle());
|
$this->assertEquals('Title', $formattedTitle->getStyle());
|
||||||
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $formattedTitle->getText());
|
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $formattedTitle->getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test reading Drawing
|
||||||
|
*/
|
||||||
|
public function testReadDrawing()
|
||||||
|
{
|
||||||
|
$documentXml = '<w:p>
|
||||||
|
<w:r>
|
||||||
|
<w:drawing xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing">
|
||||||
|
<wp:inline distT="0" distB="0" distL="0" distR="0">
|
||||||
|
<wp:extent cx="5727700" cy="6621145"/>
|
||||||
|
<wp:docPr id="1" name="Picture 1"/>
|
||||||
|
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
|
||||||
|
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
|
||||||
|
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
|
||||||
|
<pic:nvPicPr>
|
||||||
|
<pic:cNvPr id="1" name="file_name.jpg"/>
|
||||||
|
<pic:cNvPicPr/>
|
||||||
|
</pic:nvPicPr>
|
||||||
|
<pic:blipFill>
|
||||||
|
<a:blip r:embed="rId4" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
||||||
|
</a:blip>
|
||||||
|
</pic:blipFill>
|
||||||
|
</pic:pic>
|
||||||
|
</a:graphicData>
|
||||||
|
</a:graphic>
|
||||||
|
</wp:inline>
|
||||||
|
</w:drawing>
|
||||||
|
</w:r>
|
||||||
|
</w:p>';
|
||||||
|
|
||||||
|
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
|
||||||
|
|
||||||
|
$elements = $phpWord->getSection(0)->getElements();
|
||||||
|
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue