Merge branch 'master' into develop_v1.0
Conflicts: .travis.yml composer.json
This commit is contained in:
commit
847520f380
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help improve PHPWord
|
||||
labels: Bug Report
|
||||
|
||||
---
|
||||
|
||||
### Describe the Bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Please provide a code sample that reproduces the issue.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$section->...
|
||||
```
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
### Current Behavior
|
||||
|
||||
What is the current behavior?
|
||||
|
||||
### Context
|
||||
|
||||
Please fill in your environment information:
|
||||
|
||||
- PHP Version:
|
||||
- PHPWord Version:
|
||||
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels: Change Request
|
||||
|
||||
---
|
||||
|
||||
### Is your feature request related to a problem? Please describe.
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
14
.github/ISSUE_TEMPLATE/how-to-use.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/how-to-use.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
name: How to Use PHPWord
|
||||
about: Find out how to use PHPWord
|
||||
labels: WontFix
|
||||
|
||||
---
|
||||
|
||||
***Please do not use the issue tracker to ask how to use PHPWord.***
|
||||
|
||||
Documentation is available on [Read the Docs](https://phpword.readthedocs.io/en/latest/).
|
||||
|
||||
Sample code is in the [`/samples/` directory](https://github.com/PHPOffice/PHPWord/tree/develop/samples).
|
||||
|
||||
Usage questions belong on [Stack Overflow](https://stackoverflow.com/questions/tagged/phpword).
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,7 +13,6 @@ composer.phar
|
||||
vendor
|
||||
/report
|
||||
/build
|
||||
/samples/resources
|
||||
/samples/results
|
||||
/.settings
|
||||
phpword.ini
|
||||
|
||||
12
.travis.yml
12
.travis.yml
@ -1,13 +1,12 @@
|
||||
language: php
|
||||
|
||||
dist: precise
|
||||
dist: xenial
|
||||
|
||||
php:
|
||||
- 5.6
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- nightly
|
||||
- 7.4snapshot
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@ -15,14 +14,14 @@ matrix:
|
||||
env: COVERAGE=1
|
||||
- php: 7.3
|
||||
env: DEPENDENCIES="--ignore-platform-reqs"
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
env: DEPENDENCIES="--ignore-platform-reqs"
|
||||
exclude:
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@ -35,6 +34,7 @@ env:
|
||||
|
||||
before_install:
|
||||
## Packages
|
||||
- sudo rm -f /etc/apt/sources.list.d/mongodb.list # Makes apt crash on Precise, and we don't need MongoDB
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y graphviz
|
||||
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@ -3,6 +3,34 @@ Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
v0.17.0 (01 oct 2019)
|
||||
----------------------
|
||||
### Added
|
||||
- Add methods setValuesFromArray and cloneRowFromArray to the TemplateProcessor @geraldb-nicat #670
|
||||
- Set complex type in template @troosan #1565
|
||||
- implement support for section vAlign @troosan #1569
|
||||
- ParseStyle for border-color @Gllrm0 #1551
|
||||
- Html writer auto invert text color @SailorMax #1387
|
||||
- Add RightToLeft table presentation. @troosan #1550
|
||||
- Add support for page vertical alignment. @troosan #672 #1569
|
||||
- Adding setNumId method for ListItem style @eweso #1329
|
||||
- Add support for basic fields in RTF writer. @Samuel-BF #1717
|
||||
|
||||
### Fixed
|
||||
- Fix HTML border-color parsing. @troosan #1551 #1570
|
||||
- Language::validateLocale should pass with locale 'zxx'. @efpapado #1558
|
||||
- can't align center vertically with the text @ter987 #672
|
||||
- fix parsing of border-color and add test @troosan #1570
|
||||
- TrackChange doesn't handle all return types of \DateTime::createFromFormat(...) @superhaggis #1584
|
||||
- To support PreserveText inside sub container @bhattnishant #1637
|
||||
- No nested w:pPr elements in ListItemRun. @waltertamboer #1628
|
||||
- Ensure that entity_loader disable variable is re-set back to the original setting @seamuslee001 #1585
|
||||
|
||||
### Miscellaneous
|
||||
- Use embedded http server to test loading of remote images @troosan #1544
|
||||
- Change private to protected to be able extending class Html @SpinyMan #1646
|
||||
- Fix apt-get crash in Travis CI for PHP 5.3 @mdupont #1707
|
||||
|
||||
v0.16.0 (30 dec 2018)
|
||||
----------------------
|
||||
### Added
|
||||
@ -27,7 +55,7 @@ v0.16.0 (30 dec 2018)
|
||||
- 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
|
||||
### Miscellaneous
|
||||
- Get rid of duplicated code in TemplateProcessor @abcdmitry #1161
|
||||
|
||||
v0.15.0 (14 Jul 2018)
|
||||
@ -73,7 +101,7 @@ v0.15.0 (14 Jul 2018)
|
||||
- Remove zend-stdlib dependency @Trainmaster #1284
|
||||
- The default unit for `\PhpOffice\PhpWord\Style\Image` changed from `px` to `pt`.
|
||||
|
||||
### Miscelaneous
|
||||
### Miscellaneous
|
||||
- Drop GitHub pages, switch to coveralls for code coverage analysis @czosel #1360
|
||||
|
||||
v0.14.0 (29 Dec 2017)
|
||||
|
||||
@ -1,13 +1,30 @@
|
||||
# Contributing to PHPWord
|
||||
|
||||
PHPWord is built by the crowd and for the crowd. Every contribution is welcome; either by [submitting](https://github.com/PHPOffice/PHPWord/issues) bug issues or suggesting improvements, or in a more active form like [requesting](https://github.com/PHPOffice/PHPWord/pulls) a pull.
|
||||
PHPWord is built by the crowd and for the crowd. Every contribution is welcome; either by [reporting a bug](https://github.com/PHPOffice/PHPWord/issues/new?labels=Bug+Report&template=bug_report.md) or [suggesting improvements](https://github.com/PHPOffice/PHPWord/issues/new?labels=Change+Request&template=feature_request.md), or in a more active form like [requesting a pull](https://github.com/PHPOffice/PHPWord/pulls).
|
||||
|
||||
We want to create a high quality document writer and reader library that people can use with more confidence and less bugs. We want to collaborate happily, code joyfully, and get alive merrily. Thus, below are some guidelines, that we expect to be followed by each contributor.
|
||||
We want to create a high quality document writer and reader library that people can use with more confidence and fewer bugs. We want to collaborate happily, code joyfully, and live merrily. Thus, below are some guidelines that we expect to be followed by each contributor:
|
||||
|
||||
- **Be brief, but be bold**. State your issues briefly. But speak out your ideas loudly, even if you can't or don't know how to implement it right away. The world will be better with limitless innovations.
|
||||
- **Follow PHP-FIG standards**. We follow PHP Standards Recommendations (PSRs) by [PHP Framework Interoperability Group](http://www.php-fig.org/). If you're not familiar with these standards, please, [familiarize yourself now](https://github.com/php-fig/fig-standards). Also, please, use [PHPCodeSniffer](http://pear.php.net/package/PHP_CodeSniffer/) to validate your code against PSRs.
|
||||
- **Test your code**. Nobody else knows your code better than you. So, it's completely your mission to test the changes you made before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and recommend you using this tool too. [Here](https://phpunit.de/presentations.html) you can find PHPUnit best practices and additional information on effective unit testing, which helps us making PHPWord better day to day. Do not hesitate to smoke it carefully. It's a great investment in quality of your work, and it saves you years of life.
|
||||
- **Request pull in separate branch**. Do not submit your request to the master branch. But create a separate branch named specifically for the issue that you addressed. Read [GitHub manual](https://help.github.com/articles/using-pull-requests) to find out more about this. If you are new to GitHub, read [this short manual](https://help.github.com/articles/fork-a-repo) to get yourself familiar with forks and how git works in general. [This video](http://www.youtube.com/watch?v=-zvHQXnBO6c) explains how to synchronize your Github Fork with the Branch of PHPWord.
|
||||
- **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 them 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, [familiarize yourself now](https://github.com/php-fig/fig-standards). Also, please run `composer fix` to automatically fix your code to match these recommendations.
|
||||
- **Test your code**. No one knows your code better than you, so we depend on you to test the changes you make before pull request submission. We use [PHPUnit](https://phpunit.de/) for our testing purposes and request that you use this tool too. Tests can be ran with `composer test`. [Documentation for writing tests with PHPUnit is available on Read the Docs.](https://phpunit.readthedocs.io)
|
||||
- **Use best practices when submitting pull requests**. Create a separate branch named specifically for the issue that you are addressing. Read the [GitHub manual](https://help.github.com/articles/about-pull-requests) to learn more about pull requests and GitHub. 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 fork on GitHub with the upstream branch from PHPWord.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. [Clone](https://help.github.com/en/articles/cloning-a-repository) [PHPWord](https://github.com/PHPOffice/PHPWord/)
|
||||
2. [Install Composer](https://getcomposer.org/download/) if you don't already have it
|
||||
3. Open your terminal and:
|
||||
1. Switch to the directory PHPWord was cloned to (e.g., `cd ~/Projects/PHPWord/`)
|
||||
2. Run `composer install` to install the dependencies
|
||||
|
||||
You're ready to start working on PHPWord! Tests belong in the `/tests/PhpWord/` directory, the source code is in `/src/PhpWord/`, and any documentation should go in `/docs/`. Familiarize yourself with the codebase and try your hand at fixing [one of our outstanding issues](https://github.com/PHPOffice/PHPWord/issues). Before you get started, check the [existing pull requests](https://github.com/PHPOffice/PHPWord/pulls) to make sure no one else is already working on it.
|
||||
|
||||
Once you have an issue you want to start working on, you'll need to write tests for it, and then you can start implementing the changes necessary to pass the new tests. To run the tests, you can run one of the following commands in your terminal:
|
||||
|
||||
- `composer test-no-coverage` to run all of the tests
|
||||
- `composer test` to run all of the tests and generate test coverage reports
|
||||
|
||||
When you're ready to submit your new (and fully tested) feature, ensure `composer check` passes and [submit a pull request to PHPWord](https://github.com/PHPOffice/PHPWord/issues/new).
|
||||
|
||||
That's it. Thank you for your interest in PHPWord, and welcome!
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ 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 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](COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://travis-ci.org/PHPOffice/PHPWord) and [unit testing](http://phpoffice.github.io/PHPWord/coverage/develop/). You can learn more about PHPWord by reading the [Developers' Documentation](http://phpword.readthedocs.org/).
|
||||
|
||||
If you have any questions, please ask on [StackOverFlow](https://stackoverflow.com/questions/tagged/phpword)
|
||||
|
||||
@ -73,7 +73,7 @@ PHPWord requires the following:
|
||||
## Installation
|
||||
|
||||
PHPWord is installed via [Composer](https://getcomposer.org/).
|
||||
To [add a dependency](https://getcomposer.org/doc/04-schema.md#package-links>) to PHPWord in your project, either
|
||||
To [add a dependency](https://getcomposer.org/doc/04-schema.md#package-links) to PHPWord in your project, either
|
||||
|
||||
Run the following to use the latest stable version
|
||||
```sh
|
||||
@ -174,7 +174,7 @@ You can also read the [Developers' Documentation](http://phpword.readthedocs.org
|
||||
|
||||
We welcome everyone to contribute to PHPWord. Below are some of the things that you can do to contribute.
|
||||
|
||||
- Read [our contributing guide](https://github.com/PHPOffice/PHPWord/blob/master/CONTRIBUTING.md).
|
||||
- Read [our contributing guide](CONTRIBUTING.md).
|
||||
- [Fork us](https://github.com/PHPOffice/PHPWord/fork) and [request a pull](https://github.com/PHPOffice/PHPWord/pulls) to the [develop](https://github.com/PHPOffice/PHPWord/tree/develop) branch.
|
||||
- Submit [bug reports or feature requests](https://github.com/PHPOffice/PHPWord/issues) to GitHub.
|
||||
- Follow [@PHPWord](https://twitter.com/PHPWord) and [@PHPOffice](https://twitter.com/PHPOffice) on Twitter.
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
"fix": "Fixes issues found by PHP-CS"
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"php": "^7.0",
|
||||
"ext-xml": "*",
|
||||
"zendframework/zend-escaper": "^2.2",
|
||||
"phpoffice/common": "^0.2.9"
|
||||
@ -66,9 +66,9 @@
|
||||
"require-dev": {
|
||||
"ext-zip": "*",
|
||||
"ext-gd": "*",
|
||||
"phpunit/phpunit": "^5.7 || ^7.0",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"squizlabs/php_codesniffer": "^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.3",
|
||||
"friendsofphp/php-cs-fixer": "^2.14",
|
||||
"phpmd/phpmd": "2.*",
|
||||
"phploc/phploc": "^4.0",
|
||||
"dompdf/dompdf":"0.8.*",
|
||||
@ -90,7 +90,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "0.17-dev",
|
||||
"dev-develop": "0.18-dev",
|
||||
"develop_v1.0": "1.0-dev"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
This is:
|
||||
|
||||
- [ ] a bug report
|
||||
- [ ] a feature request
|
||||
- [ ] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpword)
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
Please describe the behavior you are expecting.
|
||||
|
||||
### Current Behavior
|
||||
|
||||
What is the current behavior?
|
||||
|
||||
### Failure Information
|
||||
|
||||
Please help provide information about the failure.
|
||||
|
||||
### How to Reproduce
|
||||
|
||||
Please provide a code sample that reproduces the issue.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$section->...
|
||||
```
|
||||
|
||||
### Context
|
||||
|
||||
* PHP version:
|
||||
* PHPWord version: 0.14
|
||||
@ -48,7 +48,7 @@ copyright = u'2014-2017, PHPWord Contributors'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.16.0'
|
||||
version = '0.17.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Example:
|
||||
|
||||
{
|
||||
"require": {
|
||||
"phpoffice/phpword": "v0.14.*"
|
||||
"phpoffice/phpword": "v0.17.*"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,8 @@ Available Section style options:
|
||||
See ``\PhpOffice\PhpWord\Style\Section::ORIENTATION_...`` class constants for possible values
|
||||
- ``pageSizeH``. Page height in *twip*. Implicitly defined by ``orientation`` option. Any changes are discouraged.
|
||||
- ``pageSizeW``. Page width in *twip*. Implicitly defined by ``orientation`` option. Any changes are discouraged.
|
||||
- ``vAlign``. Vertical Page Alignment
|
||||
See ``\PhpOffice\PhpWord\SimpleType\VerticalJc`` for possible values
|
||||
|
||||
.. _font-style:
|
||||
|
||||
@ -108,11 +110,12 @@ Available Table style options:
|
||||
- ``border(Top|Right|Bottom|Left)Size``. Border size in *twip*.
|
||||
- ``cellMargin(Top|Right|Bottom|Left)``. Cell margin in *twip*.
|
||||
- ``indent``. Table indent from leading margin. Must be an instance of ``\PhpOffice\PhpWord\ComplexType\TblWidth``.
|
||||
- ``width``. Table width in percent.
|
||||
- ``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*.
|
||||
- ``layout``. Table layout, either *fixed* or *autofit* See ``\PhpOffice\PhpWord\Style\Table`` for constants.
|
||||
- ``cellSpacing`` Cell spacing in *twip*
|
||||
- ``position`` Floating Table Positioning, see below for options
|
||||
- ``bidiVisual`` Present table as Right-To-Left
|
||||
|
||||
Floating Table Positioning options:
|
||||
|
||||
|
||||
@ -17,13 +17,23 @@ Given a template containing
|
||||
|
||||
.. code-block:: clean
|
||||
|
||||
Hello ${name}!
|
||||
Hello ${firstname} ${lastname}!
|
||||
|
||||
The following will replace ``${name}`` with ``World``. The resulting document will now contain ``Hello World!``
|
||||
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('name', 'World');
|
||||
$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
|
||||
"""""""""""""
|
||||
@ -138,11 +148,11 @@ See ``Sample_07_TemplateCloneRow.php`` for an example.
|
||||
|
||||
.. code-block:: clean
|
||||
|
||||
------------------------------
|
||||
+-----------+----------------+
|
||||
| ${userId} | ${userName} |
|
||||
| |----------------|
|
||||
| |----------------+
|
||||
| | ${userAddress} |
|
||||
------------------------------
|
||||
+-----------+----------------+
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -152,15 +162,49 @@ 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
|
||||
""""""""""""""""""
|
||||
@ -171,3 +215,32 @@ Applies the XSL stylesheet passed to header part, footer part and main part
|
||||
$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);
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<rule ref="rulesets/design.xml/CouplingBetweenObjects">
|
||||
<!-- AbstractContainer needs more coupling (default: 13) -->
|
||||
<properties>
|
||||
<property name="minimum" value="20" />
|
||||
<property name="maximum" value="20" />
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/design.xml/NumberOfChildren">
|
||||
@ -28,6 +28,6 @@
|
||||
<property name="minimum" value="30" />
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/unusedcode.xml" />
|
||||
<!--rule ref="rulesets/unusedcode.xml" /-->
|
||||
<rule ref="rulesets/controversial.xml" />
|
||||
</ruleset>
|
||||
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
|
||||
include_once 'Sample_Header.php';
|
||||
|
||||
// New Word Document
|
||||
@ -21,6 +23,12 @@ $section = $phpWord->addSection(
|
||||
);
|
||||
$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
|
||||
$section = $phpWord->addSection(
|
||||
array(
|
||||
|
||||
@ -36,22 +36,46 @@ $templateProcessor->setValue('rowNumber#9', '9');
|
||||
$templateProcessor->setValue('rowNumber#10', '10');
|
||||
|
||||
// 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->setValue('userFirstName#1', 'James');
|
||||
$templateProcessor->setValue('userName#1', 'Taylor');
|
||||
$templateProcessor->setValue('userPhone#1', '+1 428 889 773');
|
||||
$templateProcessor->cloneRowAndSetValues('userId', $values);
|
||||
|
||||
$templateProcessor->setValue('userId#2', '2');
|
||||
$templateProcessor->setValue('userFirstName#2', 'Robert');
|
||||
$templateProcessor->setValue('userName#2', 'Bell');
|
||||
$templateProcessor->setValue('userPhone#2', '+1 428 889 774');
|
||||
//this is equivalent to cloning and settings values with cloneRowAndSetValues
|
||||
// $templateProcessor->cloneRow('userId', 3);
|
||||
|
||||
$templateProcessor->setValue('userId#3', '3');
|
||||
$templateProcessor->setValue('userFirstName#3', 'Michael');
|
||||
$templateProcessor->setValue('userName#3', 'Ray');
|
||||
$templateProcessor->setValue('userPhone#3', '+1 428 889 775');
|
||||
// $templateProcessor->setValue('userId#1', '1');
|
||||
// $templateProcessor->setValue('userFirstName#1', 'James');
|
||||
// $templateProcessor->setValue('userName#1', 'Taylor');
|
||||
// $templateProcessor->setValue('userPhone#1', '+1 428 889 773');
|
||||
|
||||
// $templateProcessor->setValue('userId#2', '2');
|
||||
// $templateProcessor->setValue('userFirstName#2', 'Robert');
|
||||
// $templateProcessor->setValue('userName#2', 'Bell');
|
||||
// $templateProcessor->setValue('userPhone#2', '+1 428 889 774');
|
||||
|
||||
// $templateProcessor->setValue('userId#3', '3');
|
||||
// $templateProcessor->setValue('userFirstName#3', 'Michael');
|
||||
// $templateProcessor->setValue('userName#3', 'Ray');
|
||||
// $templateProcessor->setValue('userPhone#3', '+1 428 889 775');
|
||||
|
||||
echo date('H:i:s'), ' Saving the result document...', EOL;
|
||||
$templateProcessor->saveAs('results/Sample_07_TemplateCloneRow.docx');
|
||||
|
||||
@ -74,7 +74,7 @@ $html .= '<table align="center" style="width: 50%; border: 6px #0000FF double;">
|
||||
</tr>
|
||||
</thead>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>';
|
||||
|
||||
@ -14,6 +14,29 @@ $textrun->addText('This is a Left to Right paragraph.');
|
||||
$textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END));
|
||||
$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
|
||||
echo write($phpWord, basename(__FILE__, '.php'), $writers);
|
||||
if (!CLI) {
|
||||
|
||||
45
samples/Sample_40_TemplateSetComplexValue.php
Normal file
45
samples/Sample_40_TemplateSetComplexValue.php
Normal file
@ -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>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
<?php
|
||||
}
|
||||
if (!CLI) {
|
||||
echo '<h3>Requirement check:</h3>';
|
||||
|
||||
BIN
samples/resources/Sample_40_TemplateSetComplexValue.docx
Normal file
BIN
samples/resources/Sample_40_TemplateSetComplexValue.docx
Normal file
Binary file not shown.
@ -31,7 +31,7 @@ namespace PhpOffice\PhpWord\Element;
|
||||
* @method Footnote addFootnote(mixed $pStyle = null)
|
||||
* @method Endnote addEndnote(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 PageBreak addPageBreak()
|
||||
* @method Table addTable(mixed $style = null)
|
||||
@ -41,7 +41,7 @@ namespace PhpOffice\PhpWord\Element;
|
||||
* @method Field addField(string $type = null, array $properties = array(), array $options = array(), mixed $text = null)
|
||||
* @method Line addLine(mixed $lineStyle = null)
|
||||
* @method Shape addShape(string $type, mixed $style = null)
|
||||
* @method Chart addChart(string $type, array $categories, array $values, array $style = null)
|
||||
* @method Chart addChart(string $type, array $categories, array $values, array $style = null, $seriesName = null)
|
||||
* @method FormField addFormField(string $type, mixed $fStyle = null, mixed $pStyle = null)
|
||||
* @method SDT addSDT(string $type)
|
||||
*
|
||||
@ -254,7 +254,7 @@ abstract class AbstractContainer extends AbstractElement
|
||||
// Special condition, e.g. preservetext can only exists in cell when
|
||||
// the cell is located in header or footer
|
||||
$validSubcontainers = array(
|
||||
'PreserveText' => array(array('Cell'), array('Header', 'Footer')),
|
||||
'PreserveText' => array(array('Cell'), array('Header', 'Footer', 'Section')),
|
||||
'Footnote' => array(array('Cell', 'TextRun'), array('Section')),
|
||||
'Endnote' => array(array('Cell', 'TextRun'), array('Section')),
|
||||
);
|
||||
|
||||
@ -96,7 +96,7 @@ abstract class AbstractElement
|
||||
/**
|
||||
* A reference to the parent
|
||||
*
|
||||
* @var \PhpOffice\PhpWord\Element\AbstractElement
|
||||
* @var AbstractElement|null
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
@ -335,6 +335,11 @@ abstract class AbstractElement
|
||||
$this->commentRangeEnd->setEndElement($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent element
|
||||
*
|
||||
* @return AbstractElement|null
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
|
||||
@ -58,13 +58,13 @@ class TrackChange extends AbstractContainer
|
||||
*
|
||||
* @param string $changeType
|
||||
* @param string $author
|
||||
* @param null|int|\DateTime $date
|
||||
* @param null|int|bool|\DateTime $date
|
||||
*/
|
||||
public function __construct($changeType = null, $author = null, $date = null)
|
||||
{
|
||||
$this->changeType = $changeType;
|
||||
$this->author = $author;
|
||||
if ($date !== null) {
|
||||
if ($date !== null && $date !== false) {
|
||||
$this->date = ($date instanceof \DateTime) ? $date : new \DateTime('@' . $date);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,6 +483,7 @@ abstract class AbstractPart
|
||||
$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['bidiVisual'] = array(self::READ_TRUE, 'w:bidiVisual');
|
||||
$styleDefs['cellSpacing'] = array(self::READ_VALUE, 'w:tblCellSpacing', 'w:w');
|
||||
$style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs);
|
||||
|
||||
|
||||
@ -106,6 +106,7 @@ class Document extends AbstractPart
|
||||
{
|
||||
$styleDefs = array(
|
||||
'breakType' => array(self::READ_VALUE, 'w:type'),
|
||||
'vAlign' => array(self::READ_VALUE, 'w:vAlign'),
|
||||
'pageSizeW' => array(self::READ_VALUE, 'w:pgSz', 'w:w'),
|
||||
'pageSizeH' => array(self::READ_VALUE, 'w:pgSz', 'w:h'),
|
||||
'orientation' => array(self::READ_VALUE, 'w:pgSz', 'w:orient'),
|
||||
|
||||
@ -32,9 +32,9 @@ use PhpOffice\PhpWord\Style\Paragraph;
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
private static $listIndex = 0;
|
||||
private static $xpath;
|
||||
private static $options;
|
||||
protected static $listIndex = 0;
|
||||
protected static $xpath;
|
||||
protected static $options;
|
||||
|
||||
/**
|
||||
* Add HTML parts.
|
||||
@ -75,7 +75,7 @@ class Html
|
||||
$html = preg_replace('/(\>)\s*(\<)/m', '$1$2', $html);
|
||||
|
||||
// Load DOM
|
||||
libxml_disable_entity_loader(true);
|
||||
$orignalLibEntityLoader = libxml_disable_entity_loader(true);
|
||||
$dom = new \DOMDocument();
|
||||
$dom->preserveWhiteSpace = $preserveWhiteSpace;
|
||||
$dom->loadHTML($html, LIBXML_NOWARNING);
|
||||
@ -83,6 +83,7 @@ class Html
|
||||
$node = $dom->getElementsByTagName('body');
|
||||
|
||||
self::parseNode($node->item(0), $element);
|
||||
libxml_disable_entity_loader($orignalLibEntityLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,7 +195,7 @@ class Html
|
||||
$newElement = $element;
|
||||
}
|
||||
|
||||
self::parseChildNodes($node, $newElement, $styles, $data);
|
||||
static::parseChildNodes($node, $newElement, $styles, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,7 +206,7 @@ class Html
|
||||
* @param array $styles
|
||||
* @param array $data
|
||||
*/
|
||||
private static function parseChildNodes($node, $element, $styles, $data)
|
||||
protected static function parseChildNodes($node, $element, $styles, $data)
|
||||
{
|
||||
if ('li' != $node->nodeName) {
|
||||
$cNodes = $node->childNodes;
|
||||
@ -227,7 +228,7 @@ class Html
|
||||
* @param array &$styles
|
||||
* @return \PhpOffice\PhpWord\Element\TextRun
|
||||
*/
|
||||
private static function parseParagraph($node, $element, &$styles)
|
||||
protected static function parseParagraph($node, $element, &$styles)
|
||||
{
|
||||
$styles['paragraph'] = self::recursiveParseStylesInHierarchy($node, $styles['paragraph']);
|
||||
$newElement = $element->addTextRun($styles['paragraph']);
|
||||
@ -246,7 +247,7 @@ class Html
|
||||
* @todo Think of a clever way of defining header styles, now it is only based on the assumption, that
|
||||
* Heading1 - Heading6 are already defined somewhere
|
||||
*/
|
||||
private static function parseHeading($element, &$styles, $argument1)
|
||||
protected static function parseHeading($element, &$styles, $argument1)
|
||||
{
|
||||
$styles['paragraph'] = $argument1;
|
||||
$newElement = $element->addTextRun($styles['paragraph']);
|
||||
@ -261,7 +262,7 @@ class Html
|
||||
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
|
||||
* @param array &$styles
|
||||
*/
|
||||
private static function parseText($node, $element, &$styles)
|
||||
protected static function parseText($node, $element, &$styles)
|
||||
{
|
||||
$styles['font'] = self::recursiveParseStylesInHierarchy($node, $styles['font']);
|
||||
|
||||
@ -282,7 +283,7 @@ class Html
|
||||
* @param string $argument1 Style name
|
||||
* @param string $argument2 Style value
|
||||
*/
|
||||
private static function parseProperty(&$styles, $argument1, $argument2)
|
||||
protected static function parseProperty(&$styles, $argument1, $argument2)
|
||||
{
|
||||
$styles['font'][$argument1] = $argument2;
|
||||
}
|
||||
@ -293,7 +294,7 @@ class Html
|
||||
* @param \DOMNode $node
|
||||
* @param array &$styles
|
||||
*/
|
||||
private static function parseSpan($node, &$styles)
|
||||
protected static function parseSpan($node, &$styles)
|
||||
{
|
||||
self::parseInlineStyle($node, $styles['font']);
|
||||
}
|
||||
@ -308,7 +309,7 @@ class Html
|
||||
*
|
||||
* @todo As soon as TableItem, RowItem and CellItem support relative width and height
|
||||
*/
|
||||
private static function parseTable($node, $element, &$styles)
|
||||
protected static function parseTable($node, $element, &$styles)
|
||||
{
|
||||
$elementStyles = self::parseInlineStyle($node, $styles['table']);
|
||||
|
||||
@ -337,7 +338,7 @@ class Html
|
||||
* @param array &$styles
|
||||
* @return Row $element
|
||||
*/
|
||||
private static function parseRow($node, $element, &$styles)
|
||||
protected static function parseRow($node, $element, &$styles)
|
||||
{
|
||||
$rowStyles = self::parseInlineStyle($node, $styles['row']);
|
||||
if ($node->parentNode->nodeName == 'thead') {
|
||||
@ -355,7 +356,7 @@ class Html
|
||||
* @param array &$styles
|
||||
* @return \PhpOffice\PhpWord\Element\Cell|\PhpOffice\PhpWord\Element\TextRun $element
|
||||
*/
|
||||
private static function parseCell($node, $element, &$styles)
|
||||
protected static function parseCell($node, $element, &$styles)
|
||||
{
|
||||
$cellStyles = self::recursiveParseStylesInHierarchy($node, $styles['cell']);
|
||||
|
||||
@ -378,7 +379,7 @@ class Html
|
||||
* @param \DOMNode $node
|
||||
* @return bool Returns true if the node contains an HTML element that cannot be added to TextRun
|
||||
*/
|
||||
private static function shouldAddTextRun(\DOMNode $node)
|
||||
protected static function shouldAddTextRun(\DOMNode $node)
|
||||
{
|
||||
$containsBlockElement = self::$xpath->query('.//table|./p|./ul|./ol', $node)->length > 0;
|
||||
if ($containsBlockElement) {
|
||||
@ -395,7 +396,7 @@ class Html
|
||||
* @param \DOMNode $node
|
||||
* @param array &$styles
|
||||
*/
|
||||
private static function recursiveParseStylesInHierarchy(\DOMNode $node, array $style)
|
||||
protected static function recursiveParseStylesInHierarchy(\DOMNode $node, array $style)
|
||||
{
|
||||
$parentStyle = self::parseInlineStyle($node, array());
|
||||
$style = array_merge($parentStyle, $style);
|
||||
@ -414,7 +415,7 @@ class Html
|
||||
* @param array &$styles
|
||||
* @param array &$data
|
||||
*/
|
||||
private static function parseList($node, $element, &$styles, &$data)
|
||||
protected static function parseList($node, $element, &$styles, &$data)
|
||||
{
|
||||
$isOrderedList = $node->nodeName === 'ol';
|
||||
if (isset($data['listdepth'])) {
|
||||
@ -433,7 +434,7 @@ class Html
|
||||
* @param bool $isOrderedList
|
||||
* @return array
|
||||
*/
|
||||
private static function getListStyle($isOrderedList)
|
||||
protected static function getListStyle($isOrderedList)
|
||||
{
|
||||
if ($isOrderedList) {
|
||||
return array(
|
||||
@ -479,7 +480,7 @@ class Html
|
||||
* @todo This function is almost the same like `parseChildNodes`. Merged?
|
||||
* @todo As soon as ListItem inherits from AbstractContainer or TextRun delete parsing part of childNodes
|
||||
*/
|
||||
private static function parseListItem($node, $element, &$styles, $data)
|
||||
protected static function parseListItem($node, $element, &$styles, $data)
|
||||
{
|
||||
$cNodes = $node->childNodes;
|
||||
if (!empty($cNodes)) {
|
||||
@ -497,7 +498,7 @@ class Html
|
||||
* @param array $styles
|
||||
* @return array
|
||||
*/
|
||||
private static function parseStyle($attribute, $styles)
|
||||
protected static function parseStyle($attribute, $styles)
|
||||
{
|
||||
$properties = explode(';', trim($attribute->value, " \t\n\r\0\x0B;"));
|
||||
|
||||
@ -519,7 +520,7 @@ class Html
|
||||
$styles['alignment'] = self::mapAlign($cValue);
|
||||
break;
|
||||
case 'display':
|
||||
$styles['hidden'] = $cValue === 'none';
|
||||
$styles['hidden'] = $cValue === 'none' || $cValue === 'hidden';
|
||||
break;
|
||||
case 'direction':
|
||||
$styles['rtl'] = $cValue === 'rtl';
|
||||
@ -584,7 +585,7 @@ class Html
|
||||
$styles['spaceAfter'] = Converter::cssToPoint($cValue);
|
||||
break;
|
||||
case 'border-color':
|
||||
$styles['color'] = trim($cValue, '#');
|
||||
self::mapBorderColor($styles, $cValue);
|
||||
break;
|
||||
case 'border-width':
|
||||
$styles['borderSize'] = Converter::cssToPoint($cValue);
|
||||
@ -625,7 +626,7 @@ class Html
|
||||
*
|
||||
* @return \PhpOffice\PhpWord\Element\Image
|
||||
**/
|
||||
private static function parseImage($node, $element)
|
||||
protected static function parseImage($node, $element)
|
||||
{
|
||||
$style = array();
|
||||
$src = null;
|
||||
@ -728,7 +729,7 @@ class Html
|
||||
* @param string $cssBorderStyle
|
||||
* @return null|string
|
||||
*/
|
||||
private static function mapBorderStyle($cssBorderStyle)
|
||||
protected static function mapBorderStyle($cssBorderStyle)
|
||||
{
|
||||
switch ($cssBorderStyle) {
|
||||
case 'none':
|
||||
@ -741,13 +742,27 @@ class Html
|
||||
}
|
||||
}
|
||||
|
||||
protected 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
|
||||
*
|
||||
* @param string $cssAlignment
|
||||
* @return string|null
|
||||
*/
|
||||
private static function mapAlign($cssAlignment)
|
||||
protected static function mapAlign($cssAlignment)
|
||||
{
|
||||
switch ($cssAlignment) {
|
||||
case 'right':
|
||||
@ -766,7 +781,7 @@ class Html
|
||||
*
|
||||
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
|
||||
*/
|
||||
private static function parseLineBreak($element)
|
||||
protected static function parseLineBreak($element)
|
||||
{
|
||||
$element->addTextBreak();
|
||||
}
|
||||
@ -778,7 +793,7 @@ class Html
|
||||
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
|
||||
* @param array $styles
|
||||
*/
|
||||
private static function parseLink($node, $element, &$styles)
|
||||
protected static function parseLink($node, $element, &$styles)
|
||||
{
|
||||
$target = null;
|
||||
foreach ($node->attributes as $attribute) {
|
||||
|
||||
36
src/PhpWord/SimpleType/VerticalJc.php
Normal file
36
src/PhpWord/SimpleType/VerticalJc.php
Normal file
@ -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';
|
||||
}
|
||||
@ -18,6 +18,7 @@
|
||||
namespace PhpOffice\PhpWord\Style;
|
||||
|
||||
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
|
||||
/**
|
||||
* Table cell style
|
||||
@ -28,10 +29,20 @@ class Cell extends Border
|
||||
* Vertical alignment constants
|
||||
*
|
||||
* @const string
|
||||
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::TOP instead
|
||||
*/
|
||||
const VALIGN_TOP = 'top';
|
||||
/**
|
||||
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::CENTER instead
|
||||
*/
|
||||
const VALIGN_CENTER = 'center';
|
||||
/**
|
||||
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::BOTTOM instead
|
||||
*/
|
||||
const VALIGN_BOTTOM = 'bottom';
|
||||
/**
|
||||
* @deprecated Use \PhpOffice\PhpWord\SimpleType\VerticalJc::BOTH instead
|
||||
*/
|
||||
const VALIGN_BOTH = 'both';
|
||||
|
||||
//Text direction constants
|
||||
@ -145,8 +156,8 @@ class Cell extends Border
|
||||
*/
|
||||
public function setVAlign($value = null)
|
||||
{
|
||||
$enum = array(self::VALIGN_TOP, self::VALIGN_CENTER, self::VALIGN_BOTTOM, self::VALIGN_BOTH);
|
||||
$this->vAlign = $this->setEnumVal($value, $enum, $this->vAlign);
|
||||
VerticalJc::validate($value);
|
||||
$this->vAlign = $this->setEnumVal($value, VerticalJc::values(), $this->vAlign);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -71,6 +71,9 @@ final class Language extends AbstractStyle
|
||||
const UK_UA = 'uk-UA';
|
||||
const UK_UA_ID = 1058;
|
||||
|
||||
const RU_RU = 'ru-RU';
|
||||
const RU_RU_ID = 1049;
|
||||
|
||||
/**
|
||||
* Language ID, used for RTF document generation
|
||||
*
|
||||
@ -229,7 +232,7 @@ final class Language extends AbstractStyle
|
||||
return strtolower($locale) . '-' . strtoupper($locale);
|
||||
}
|
||||
|
||||
if ($locale !== null && strstr($locale, '-') === false) {
|
||||
if ($locale !== null && $locale !== 'zxx' && strstr($locale, '-') === false) {
|
||||
throw new \InvalidArgumentException($locale . ' is not a valid language code');
|
||||
}
|
||||
|
||||
|
||||
@ -139,6 +139,16 @@ class ListItem extends AbstractStyle
|
||||
return $this->numId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set numbering Id. Same numId means same list
|
||||
* @param mixed $numInt
|
||||
*/
|
||||
public function setNumId($numInt)
|
||||
{
|
||||
$this->numId = $numInt;
|
||||
$this->getListTypeStyle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get legacy numbering definition
|
||||
*
|
||||
@ -148,7 +158,12 @@ class ListItem extends AbstractStyle
|
||||
private function getListTypeStyle()
|
||||
{
|
||||
// Check if legacy style already registered in global Style collection
|
||||
$numStyle = "PHPWordList{$this->listType}";
|
||||
$numStyle = 'PHPWordListType' . $this->listType;
|
||||
|
||||
if ($this->numId) {
|
||||
$numStyle .= 'NumId' . $this->numId;
|
||||
}
|
||||
|
||||
if (Style::getStyle($numStyle) !== null) {
|
||||
$this->setNumStyle($numStyle);
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Style;
|
||||
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
|
||||
/**
|
||||
* Section settings
|
||||
*/
|
||||
@ -166,6 +168,14 @@ class Section extends Border
|
||||
*/
|
||||
private $lineNumbering;
|
||||
|
||||
/**
|
||||
* Vertical Text Alignment on Page
|
||||
* One of \PhpOffice\PhpWord\SimpleType\VerticalJc
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $vAlign;
|
||||
|
||||
/**
|
||||
* Create new instance
|
||||
*/
|
||||
@ -599,4 +609,28 @@ class Section extends Border
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,6 +170,14 @@ class Table extends Border
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -775,4 +783,28 @@ class Table extends Border
|
||||
{
|
||||
$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;
|
||||
|
||||
use PhpOffice\Common\Text;
|
||||
use PhpOffice\Common\XMLWriter;
|
||||
use PhpOffice\PhpWord\Escaper\RegExp;
|
||||
use PhpOffice\PhpWord\Escaper\Xml;
|
||||
use PhpOffice\PhpWord\Exception\CopyFileException;
|
||||
@ -48,6 +49,13 @@ class TemplateProcessor
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -119,6 +127,7 @@ class TemplateProcessor
|
||||
}
|
||||
|
||||
$this->tempDocumentMainPart = $this->readPartWithRels($this->getMainPartName());
|
||||
$this->tempDocumentSettingsPart = $this->readPartWithRels($this->getSettingsPartName());
|
||||
$this->tempDocumentContentTypes = $this->zipClass->getFromName($this->getDocumentContentTypesName());
|
||||
}
|
||||
|
||||
@ -161,7 +170,7 @@ class TemplateProcessor
|
||||
*/
|
||||
protected function transformSingleXml($xml, $xsltProcessor)
|
||||
{
|
||||
libxml_disable_entity_loader(true);
|
||||
$orignalLibEntityLoader = libxml_disable_entity_loader(true);
|
||||
$domDocument = new \DOMDocument();
|
||||
if (false === $domDocument->loadXML($xml)) {
|
||||
throw new Exception('Could not load the given XML document.');
|
||||
@ -171,6 +180,7 @@ class TemplateProcessor
|
||||
if (false === $transformedXml) {
|
||||
throw new Exception('Could not transform the given XML document.');
|
||||
}
|
||||
libxml_disable_entity_loader($orignalLibEntityLoader);
|
||||
|
||||
return $transformedXml;
|
||||
}
|
||||
@ -249,6 +259,46 @@ class TemplateProcessor
|
||||
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 $replace
|
||||
@ -284,6 +334,18 @@ class TemplateProcessor
|
||||
$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);
|
||||
@ -641,6 +703,24 @@ class TemplateProcessor
|
||||
$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.
|
||||
*
|
||||
@ -655,6 +735,7 @@ class TemplateProcessor
|
||||
public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
|
||||
{
|
||||
$xmlBlock = null;
|
||||
$matches = array();
|
||||
preg_match(
|
||||
'/(<\?xml.*)(<w:p\b.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p\b.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||
$this->tempDocumentMainPart,
|
||||
@ -694,6 +775,7 @@ class TemplateProcessor
|
||||
*/
|
||||
public function replaceBlock($blockname, $replacement)
|
||||
{
|
||||
$matches = array();
|
||||
preg_match(
|
||||
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||
$this->tempDocumentMainPart,
|
||||
@ -719,6 +801,22 @@ class TemplateProcessor
|
||||
$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.
|
||||
*
|
||||
@ -733,6 +831,7 @@ class TemplateProcessor
|
||||
}
|
||||
|
||||
$this->savePartWithRels($this->getMainPartName(), $this->tempDocumentMainPart);
|
||||
$this->savePartWithRels($this->getSettingsPartName(), $this->tempDocumentSettingsPart);
|
||||
|
||||
foreach ($this->tempDocumentFooters as $index => $xml) {
|
||||
$this->savePartWithRels($this->getFooterName($index), $xml);
|
||||
@ -835,6 +934,7 @@ class TemplateProcessor
|
||||
*/
|
||||
protected function getVariablesForPart($documentPartXML)
|
||||
{
|
||||
$matches = array();
|
||||
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
||||
|
||||
return $matches[1];
|
||||
@ -863,11 +963,22 @@ class TemplateProcessor
|
||||
|
||||
$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';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the footer file for $index.
|
||||
*
|
||||
@ -1001,4 +1112,141 @@ class TemplateProcessor
|
||||
|
||||
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
|
||||
$this->originalFilename = $filename;
|
||||
if (strtolower($filename) == 'php://output' || strtolower($filename) == 'php://stdout') {
|
||||
if (strpos(strtolower($filename), 'php://') === 0) {
|
||||
$filename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||
if (false === $filename) {
|
||||
$filename = $this->originalFilename; // @codeCoverageIgnore
|
||||
|
||||
@ -51,6 +51,14 @@ class Table extends AbstractElement
|
||||
$rowCellCount = count($rowCells);
|
||||
for ($j = 0; $j < $rowCellCount; $j++) {
|
||||
$cellStyle = $rowCells[$j]->getStyle();
|
||||
$cellBgColor = $cellStyle->getBgColor();
|
||||
$cellFgColor = null;
|
||||
if ($cellBgColor) {
|
||||
$red = hexdec(substr($cellBgColor, 0, 2));
|
||||
$green = hexdec(substr($cellBgColor, 2, 2));
|
||||
$blue = hexdec(substr($cellBgColor, 4, 2));
|
||||
$cellFgColor = (($red * 0.299 + $green * 0.587 + $blue * 0.114) > 186) ? null : 'ffffff';
|
||||
}
|
||||
$cellColSpan = $cellStyle->getGridSpan();
|
||||
$cellRowSpan = 1;
|
||||
$cellVMerge = $cellStyle->getVMerge();
|
||||
@ -74,7 +82,9 @@ class Table extends AbstractElement
|
||||
$cellTag = $tblHeader ? 'th' : 'td';
|
||||
$cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : '');
|
||||
$cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : '');
|
||||
$content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr}>" . PHP_EOL;
|
||||
$cellBgColorAttr = (is_null($cellBgColor) ? '' : " bgcolor=\"#{$cellBgColor}\"");
|
||||
$cellFgColorAttr = (is_null($cellFgColor) ? '' : " color=\"#{$cellFgColor}\"");
|
||||
$content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr}{$cellBgColorAttr}{$cellFgColorAttr}>" . PHP_EOL;
|
||||
$writer = new Container($this->parentWriter, $rowCells[$j]);
|
||||
$content .= $writer->write();
|
||||
if ($cellRowSpan > 1) {
|
||||
|
||||
@ -54,6 +54,10 @@ class Paragraph extends AbstractStyle
|
||||
$xmlWriter->writeAttribute('fo:margin-bottom', $marginBottom . 'cm');
|
||||
$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:style
|
||||
|
||||
@ -43,6 +43,7 @@ class Table extends AbstractStyle
|
||||
//$xmlWriter->writeAttribute('style:width', 'table');
|
||||
$xmlWriter->writeAttribute('style:rel-width', 100);
|
||||
$xmlWriter->writeAttribute('table:align', 'center');
|
||||
$xmlWriter->writeAttributeIf($style->isBidiVisual(), 'style:writing-mode', 'rl-tb');
|
||||
$xmlWriter->endElement(); // style:table-properties
|
||||
$xmlWriter->endElement(); // style:style
|
||||
|
||||
|
||||
80
src/PhpWord/Writer/RTF/Element/Field.php
Normal file
80
src/PhpWord/Writer/RTF/Element/Field.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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 2019 PHPWord contributors
|
||||
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
|
||||
*/
|
||||
|
||||
namespace PhpOffice\PhpWord\Writer\RTF\Element;
|
||||
|
||||
/**
|
||||
* Field element writer
|
||||
*
|
||||
* Note: for now, only date, page and numpages fields are implemented for RTF.
|
||||
*/
|
||||
class Field extends Text
|
||||
{
|
||||
/**
|
||||
* Write field element.
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$element = $this->element;
|
||||
if (!$element instanceof \PhpOffice\PhpWord\Element\Field) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getStyles();
|
||||
|
||||
$content = '';
|
||||
$content .= $this->writeOpening();
|
||||
$content .= '{';
|
||||
$content .= $this->writeFontStyle();
|
||||
|
||||
$methodName = 'write' . ucfirst(strtolower($element->getType()));
|
||||
if (!method_exists($this, $methodName)) {
|
||||
// Unsupported field
|
||||
$content .= '';
|
||||
} else {
|
||||
$content .= '\\field{\\*\\fldinst ';
|
||||
$content .= $this->$methodName($element);
|
||||
$content .= '}{\\fldrslt}';
|
||||
}
|
||||
$content .= '}';
|
||||
$content .= $this->writeClosing();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function writePage()
|
||||
{
|
||||
return 'PAGE';
|
||||
}
|
||||
|
||||
protected function writeNumpages()
|
||||
{
|
||||
return 'NUMPAGES';
|
||||
}
|
||||
|
||||
protected function writeDate(\PhpOffice\PhpWord\Element\Field $element)
|
||||
{
|
||||
$content = '';
|
||||
$content .= 'DATE';
|
||||
$properties = $element->getProperties();
|
||||
if (isset($properties['dateformat'])) {
|
||||
$content .= ' \\\\@ "' . $properties['dateformat'] . '"';
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Writer\Word2007\Element;
|
||||
|
||||
use PhpOffice\PhpWord\Element\ListItemRun as ListItemRunElement;
|
||||
use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter;
|
||||
|
||||
/**
|
||||
@ -31,34 +32,56 @@ class ListItemRun extends AbstractElement
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$element = $this->getElement();
|
||||
if (!$element instanceof \PhpOffice\PhpWord\Element\ListItemRun) {
|
||||
|
||||
if (!$element instanceof ListItemRunElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->writeParagraph($element);
|
||||
}
|
||||
|
||||
private function writeParagraph(ListItemRunElement $element)
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$xmlWriter->startElement('w:p');
|
||||
|
||||
$xmlWriter->startElement('w:pPr');
|
||||
$paragraphStyle = $element->getParagraphStyle();
|
||||
$styleWriter = new ParagraphStyleWriter($xmlWriter, $paragraphStyle);
|
||||
$styleWriter->setIsInline(true);
|
||||
$styleWriter->write();
|
||||
|
||||
$xmlWriter->startElement('w:numPr');
|
||||
$xmlWriter->startElement('w:ilvl');
|
||||
$xmlWriter->writeAttribute('w:val', $element->getDepth());
|
||||
$xmlWriter->endElement(); // w:ilvl
|
||||
$xmlWriter->startElement('w:numId');
|
||||
$xmlWriter->writeAttribute('w:val', $element->getStyle()->getNumId());
|
||||
$xmlWriter->endElement(); // w:numId
|
||||
$xmlWriter->endElement(); // w:numPr
|
||||
|
||||
$xmlWriter->endElement(); // w:pPr
|
||||
$this->writeParagraphProperties($element);
|
||||
|
||||
$containerWriter = new Container($xmlWriter, $element);
|
||||
$containerWriter->write();
|
||||
|
||||
$xmlWriter->endElement(); // w:p
|
||||
}
|
||||
|
||||
private function writeParagraphProperties(ListItemRunElement $element)
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$xmlWriter->startElement('w:pPr');
|
||||
|
||||
$styleWriter = new ParagraphStyleWriter($xmlWriter, $element->getParagraphStyle());
|
||||
$styleWriter->setIsInline(true);
|
||||
$styleWriter->setWithoutPPR(true);
|
||||
$styleWriter->write();
|
||||
|
||||
$this->writeParagraphPropertiesNumbering($element);
|
||||
|
||||
$xmlWriter->endElement(); // w:pPr
|
||||
}
|
||||
|
||||
private function writeParagraphPropertiesNumbering(ListItemRunElement $element)
|
||||
{
|
||||
$xmlWriter = $this->getXmlWriter();
|
||||
$xmlWriter->startElement('w:numPr');
|
||||
|
||||
$xmlWriter->writeElementBlock('w:ilvl', array(
|
||||
'w:val' => $element->getDepth(),
|
||||
));
|
||||
|
||||
$xmlWriter->writeElementBlock('w:numId', array(
|
||||
'w:val' => $element->getStyle()->getNumId(),
|
||||
));
|
||||
|
||||
$xmlWriter->endElement(); // w:numPr
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,10 @@ class Section extends AbstractStyle
|
||||
$xmlWriter->writeAttribute('w:h', $style->getPageSizeH());
|
||||
$xmlWriter->endElement(); // w:pgSz
|
||||
|
||||
// Vertical alignment
|
||||
$vAlign = $style->getVAlign();
|
||||
$xmlWriter->writeElementIf(!is_null($vAlign), 'w:vAlign', 'w:val', $vAlign);
|
||||
|
||||
// Margins
|
||||
$margins = array(
|
||||
'w:top' => array('getMarginTop', SectionStyle::DEFAULT_MARGIN),
|
||||
|
||||
@ -86,6 +86,9 @@ class Table extends AbstractStyle
|
||||
$styleWriter = new TablePosition($xmlWriter, $style->getPosition());
|
||||
$styleWriter->write();
|
||||
|
||||
//Right to left
|
||||
$xmlWriter->writeElementIf($style->isBidiVisual() !== null, 'w:bidiVisual', 'w:val', $this->writeOnOf($style->isBidiVisual()));
|
||||
|
||||
$this->writeMargin($xmlWriter, $style);
|
||||
$this->writeBorder($xmlWriter, $style);
|
||||
|
||||
|
||||
@ -17,12 +17,14 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Element;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
|
||||
/**
|
||||
* Test class for PhpOffice\PhpWord\Element\Cell
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class CellTest extends \PHPUnit\Framework\TestCase
|
||||
class CellTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* New instance
|
||||
@ -165,7 +167,7 @@ class CellTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddImageSectionByUrl()
|
||||
{
|
||||
$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->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||
@ -231,7 +233,7 @@ class CellTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddPreserveTextException()
|
||||
{
|
||||
$oCell = new Cell();
|
||||
$oCell->setDocPart('Section', 1);
|
||||
$oCell->setDocPart('TextRun', 1);
|
||||
$oCell->addPreserveText('text');
|
||||
}
|
||||
|
||||
|
||||
@ -17,12 +17,14 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Element;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
|
||||
/**
|
||||
* Test class for PhpOffice\PhpWord\Element\Footer
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class FooterTest extends \PHPUnit\Framework\TestCase
|
||||
class FooterTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* New instance
|
||||
@ -116,7 +118,7 @@ class FooterTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddImageByUrl()
|
||||
{
|
||||
$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->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||
|
||||
@ -17,12 +17,14 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Element;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
|
||||
/**
|
||||
* Test class for PhpOffice\PhpWord\Element\Header
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class HeaderTest extends \PHPUnit\Framework\TestCase
|
||||
class HeaderTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* New instance
|
||||
@ -125,7 +127,7 @@ class HeaderTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddImageByUrl()
|
||||
{
|
||||
$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->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $element);
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Element;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
|
||||
/**
|
||||
@ -24,7 +25,7 @@ use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class ImageTest extends \PHPUnit\Framework\TestCase
|
||||
class ImageTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* New instance
|
||||
@ -131,7 +132,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||
*/
|
||||
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(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
@ -139,7 +140,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||
),
|
||||
);
|
||||
stream_context_set_default($arrContextOptions);
|
||||
$object = new Image('https://samples.libav.org/image-samples/RACECAR.BMP');
|
||||
$object = new Image(self::getRemoteBmpImageUrl());
|
||||
$object->getSource();
|
||||
}
|
||||
|
||||
@ -215,7 +216,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public function testConstructFromGd()
|
||||
{
|
||||
$source = 'http://php.net/images/logos/php-icon.png';
|
||||
$source = self::getRemoteImageUrl();
|
||||
|
||||
$image = new Image($source);
|
||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image);
|
||||
|
||||
@ -41,4 +41,22 @@ class TrackChangeTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals($date, $oTrackChange->getDate());
|
||||
$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
|
||||
*/
|
||||
class MediaTest extends \PHPUnit\Framework\TestCase
|
||||
class MediaTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* Get section media elements
|
||||
@ -49,7 +49,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
$local = __DIR__ . '/_files/images/mars.jpg';
|
||||
$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', $remote, new Image($local));
|
||||
@ -77,7 +77,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddHeaderMediaElement()
|
||||
{
|
||||
$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', $remote, new Image($remote));
|
||||
@ -92,7 +92,7 @@ class MediaTest extends \PHPUnit\Framework\TestCase
|
||||
public function testAddFooterMediaElement()
|
||||
{
|
||||
$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', $remote, new Image($remote));
|
||||
|
||||
@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Reader\Word2007;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractTestReader;
|
||||
use PhpOffice\PhpWord\SimpleType\TblWidth;
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
use PhpOffice\PhpWord\Style;
|
||||
use PhpOffice\PhpWord\Style\Table;
|
||||
use PhpOffice\PhpWord\Style\TablePosition;
|
||||
@ -147,6 +148,24 @@ class StyleTest extends AbstractTestReader
|
||||
$this->assertSame(2160, $tableStyle->getIndent()->getValue());
|
||||
}
|
||||
|
||||
public function testReadTableRTL()
|
||||
{
|
||||
$documentXml = '<w:tbl>
|
||||
<w:tblPr>
|
||||
<w:bidiVisual w:val="1"/>
|
||||
</w:tblPr>
|
||||
</w:tbl>';
|
||||
|
||||
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
|
||||
|
||||
$elements = $phpWord->getSection(0)->getElements();
|
||||
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]);
|
||||
$this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle());
|
||||
/** @var \PhpOffice\PhpWord\Style\Table $tableStyle */
|
||||
$tableStyle = $elements[0]->getStyle();
|
||||
$this->assertTrue($tableStyle->isBidiVisual());
|
||||
}
|
||||
|
||||
public function testReadHidden()
|
||||
{
|
||||
$documentXml = '<w:p>
|
||||
@ -195,4 +214,16 @@ class StyleTest extends AbstractTestReader
|
||||
$this->getDocumentFromString(array('styles' => $documentXml));
|
||||
$this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Font', Style::getStyle($name));
|
||||
}
|
||||
|
||||
public function testPageVerticalAlign()
|
||||
{
|
||||
$documentXml = '<w:sectPr>
|
||||
<w:vAlign w:val="center"/>
|
||||
</w:sectPr>';
|
||||
|
||||
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
|
||||
|
||||
$sectionStyle = $phpWord->getSection(0)->getStyle();
|
||||
$this->assertEquals(VerticalJc::CENTER, $sectionStyle->getVAlign());
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Shared;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
use PhpOffice\PhpWord\Element\Section;
|
||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
|
||||
@ -27,7 +28,7 @@ use PhpOffice\PhpWord\TestHelperDOCX;
|
||||
* Test class for PhpOffice\PhpWord\Shared\Html
|
||||
* @coversDefaultClass \PhpOffice\PhpWord\Shared\Html
|
||||
*/
|
||||
class HtmlTest extends \PHPUnit\Framework\TestCase
|
||||
class HtmlTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* Test unit conversion functions with various numbers
|
||||
@ -296,8 +297,8 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
|
||||
<thead>
|
||||
<tr style="background-color: #FF0000; text-align: center; color: #FFFFFF; font-weight: bold; ">
|
||||
<th style="width: 50pt">header a</th>
|
||||
<th style="width: 50">header b</th>
|
||||
<th style="border-color: #00FF00; border-width: 3px">header c</th>
|
||||
<th style="width: 50; border-color: #00EE00">header b</th>
|
||||
<th style="border-color: #00AA00 #00BB00 #00CC00 #00DD00; border-width: 3px">header c</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -312,6 +313,17 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tr/w:tc'));
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:tbl/w:tblPr/w:jc'));
|
||||
$this->assertEquals(Jc::START, $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tblPr/w:jc', 'w:val'));
|
||||
|
||||
//check border colors
|
||||
$this->assertEquals('00EE00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[2]/w:tcPr/w:tcBorders/w:top', 'w:color'));
|
||||
$this->assertEquals('00EE00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[2]/w:tcPr/w:tcBorders/w:right', 'w:color'));
|
||||
$this->assertEquals('00EE00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[2]/w:tcPr/w:tcBorders/w:bottom', 'w:color'));
|
||||
$this->assertEquals('00EE00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[2]/w:tcPr/w:tcBorders/w:left', 'w:color'));
|
||||
|
||||
$this->assertEquals('00AA00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[3]/w:tcPr/w:tcBorders/w:top', 'w:color'));
|
||||
$this->assertEquals('00BB00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[3]/w:tcPr/w:tcBorders/w:right', 'w:color'));
|
||||
$this->assertEquals('00CC00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[3]/w:tcPr/w:tcBorders/w:bottom', 'w:color'));
|
||||
$this->assertEquals('00DD00', $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr[1]/w:tc[3]/w:tcPr/w:tcBorders/w:left', 'w:color'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -487,7 +499,7 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public function testParseRemoteImage()
|
||||
{
|
||||
$src = 'https://phpword.readthedocs.io/en/latest/_images/phpword.png';
|
||||
$src = self::getRemoteImageUrl();
|
||||
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
@ -589,4 +601,35 @@ class HtmlTest extends \PHPUnit\Framework\TestCase
|
||||
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
|
||||
$this->assertFalse($doc->elementExists('/w:document/w:body/w:p[1]/w:pPr/w:jc'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parsing hidden text
|
||||
*/
|
||||
public function testParseHiddenText()
|
||||
{
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$html = '<p style="display: hidden">This is some hidden text.</p>';
|
||||
Html::addHtml($section, $html);
|
||||
|
||||
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
|
||||
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:vanish'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parsing letter spacing
|
||||
*/
|
||||
public function testParseLetterSpacing()
|
||||
{
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$html = '<p style="letter-spacing: 150px">This is some text with letter spacing.</p>';
|
||||
Html::addHtml($section, $html);
|
||||
|
||||
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
|
||||
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:spacing'));
|
||||
$this->assertEquals(150 * 15, $doc->getElement('/w:document/w:body/w:p/w:r/w:rPr/w:spacing')->getAttribute('w:val'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Style;
|
||||
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
|
||||
/**
|
||||
* Test class for PhpOffice\PhpWord\Style\Cell
|
||||
*
|
||||
@ -33,7 +35,7 @@ class CellTest extends \PHPUnit\Framework\TestCase
|
||||
$object = new Cell();
|
||||
|
||||
$attributes = array(
|
||||
'valign' => Cell::VALIGN_TOP,
|
||||
'valign' => VerticalJc::TOP,
|
||||
'textDirection' => Cell::TEXT_DIR_BTLR,
|
||||
'bgColor' => 'FFFF00',
|
||||
'borderTopSize' => 120,
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Style;
|
||||
|
||||
use PhpOffice\PhpWord\SimpleType\VerticalJc;
|
||||
|
||||
/**
|
||||
* Test class for PhpOffice\PhpWord\Style\Section
|
||||
*
|
||||
@ -328,4 +330,18 @@ class SectionTest extends \PHPUnit\Framework\TestCase
|
||||
$oSettings->setBreakType();
|
||||
$this->assertNull($oSettings->getBreakType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical page alignment
|
||||
*/
|
||||
public function testVerticalAlign()
|
||||
{
|
||||
// Section Settings
|
||||
$oSettings = new Section();
|
||||
|
||||
$this->assertNull($oSettings->getVAlign());
|
||||
|
||||
$oSettings->setVAlign(VerticalJc::BOTH);
|
||||
$this->assertEquals('both', $oSettings->getVAlign());
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
|
||||
namespace PhpOffice\PhpWord;
|
||||
|
||||
use PhpOffice\PhpWord\Element\Text;
|
||||
use PhpOffice\PhpWord\Element\TextRun;
|
||||
|
||||
/**
|
||||
* @covers \PhpOffice\PhpWord\TemplateProcessor
|
||||
* @coversDefaultClass \PhpOffice\PhpWord\TemplateProcessor
|
||||
@ -196,6 +199,67 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertTrue($docFound);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setValue
|
||||
* @covers ::cloneRow
|
||||
* @covers ::saveAs
|
||||
* @test
|
||||
*/
|
||||
public function testCloneRowAndSetValues()
|
||||
{
|
||||
$mainPart = '<w:tbl>
|
||||
<w:tr>
|
||||
<w:tc>
|
||||
<w:tcPr>
|
||||
<w:vMerge w:val="restart"/>
|
||||
</w:tcPr>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t>${userId}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
</w:tc>
|
||||
<w:tc>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t>${userName}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
</w:tc>
|
||||
</w:tr>
|
||||
<w:tr>
|
||||
<w:tc>
|
||||
<w:tcPr>
|
||||
<w:vMerge/>
|
||||
</w:tcPr>
|
||||
<w:p/>
|
||||
</w:tc>
|
||||
<w:tc>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t>${userLocation}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
</w:tc>
|
||||
</w:tr>
|
||||
</w:tbl>';
|
||||
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||
|
||||
$this->assertEquals(
|
||||
array('userId', 'userName', 'userLocation'),
|
||||
$templateProcessor->getVariables()
|
||||
);
|
||||
|
||||
$values = array(
|
||||
array('userId' => 1, 'userName' => 'Batman', 'userLocation' => 'Gotham City'),
|
||||
array('userId' => 2, 'userName' => 'Superman', 'userLocation' => 'Metropolis'),
|
||||
);
|
||||
$templateProcessor->setValue('tableHeader', 'My clonable table');
|
||||
$templateProcessor->cloneRowAndSetValues('userId', $values);
|
||||
$this->assertContains('<w:t>Superman</w:t>', $templateProcessor->getMainPart());
|
||||
$this->assertContains('<w:t>Metropolis</w:t>', $templateProcessor->getMainPart());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @test
|
||||
@ -246,6 +310,78 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testSetComplexValue()
|
||||
{
|
||||
$title = new TextRun();
|
||||
$title->addText('This is my title');
|
||||
|
||||
$firstname = new Text('Donald');
|
||||
$lastname = new Text('Duck');
|
||||
|
||||
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t xml:space="preserve">Hello ${document-title}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t>
|
||||
</w:r>
|
||||
</w:p>';
|
||||
|
||||
$result = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<w:p>
|
||||
<w:pPr/>
|
||||
<w:r>
|
||||
<w:rPr/>
|
||||
<w:t xml:space="preserve">This is my title</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t xml:space="preserve">Hello </w:t>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:rPr/>
|
||||
<w:t xml:space="preserve">Donald</w:t>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:t xml:space="preserve"> </w:t>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:rPr/>
|
||||
<w:t xml:space="preserve">Duck</w:t>
|
||||
</w:r>
|
||||
</w:p>';
|
||||
|
||||
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||
$templateProcessor->setComplexBlock('document-title', $title);
|
||||
$templateProcessor->setComplexValue('firstname', $firstname);
|
||||
$templateProcessor->setComplexValue('lastname', $lastname);
|
||||
|
||||
$this->assertEquals(preg_replace('/>\s+</', '><', $result), preg_replace('/>\s+</', '><', $templateProcessor->getMainPart()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setValues
|
||||
* @test
|
||||
*/
|
||||
public function testSetValues()
|
||||
{
|
||||
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t>
|
||||
</w:r>
|
||||
</w:p>';
|
||||
|
||||
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||
$templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe'));
|
||||
|
||||
$this->assertContains('Hello John Doe', $templateProcessor->getMainPart());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setImageValue
|
||||
* @test
|
||||
@ -595,4 +731,117 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||
$variables = $templateProcessor->getVariablesForPart('<w:t>$</w:t></w:r><w:bookmarkStart w:id="0" w:name="_GoBack"/><w:bookmarkEnd w:id="0"/><w:r><w:t xml:space="preserve">15,000.00. </w:t></w:r><w:r w:rsidR="0056499B"><w:t>$</w:t></w:r><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>{</w:t></w:r><w:proofErr w:type="spellStart"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>variable_name</w:t></w:r><w:proofErr w:type="spellEnd"/><w:r w:rsidR="00573DFD" w:rsidRPr="00573DFD"><w:rPr><w:iCs/></w:rPr><w:t>}</w:t></w:r>');
|
||||
$this->assertEquals(array('variable_name'), $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::textNeedsSplitting
|
||||
*/
|
||||
public function testTextNeedsSplitting()
|
||||
{
|
||||
$templateProcessor = new TestableTemplateProcesor();
|
||||
|
||||
$this->assertFalse($templateProcessor->textNeedsSplitting('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>'));
|
||||
|
||||
$text = '<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t></w:r>';
|
||||
$this->assertTrue($templateProcessor->textNeedsSplitting($text));
|
||||
$splitText = $templateProcessor->splitTextIntoTexts($text);
|
||||
$this->assertFalse($templateProcessor->textNeedsSplitting($splitText));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::splitTextIntoTexts
|
||||
*/
|
||||
public function testSplitTextIntoTexts()
|
||||
{
|
||||
$templateProcessor = new TestableTemplateProcesor();
|
||||
|
||||
$splitText = $templateProcessor->splitTextIntoTexts('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>');
|
||||
$this->assertEquals('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${nothing-to-replace}</w:t></w:r>', $splitText);
|
||||
|
||||
$splitText = $templateProcessor->splitTextIntoTexts('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello ${firstname} ${lastname}</w:t></w:r>');
|
||||
$this->assertEquals('<w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">Hello </w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${firstname}</w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve"> </w:t></w:r><w:r><w:rPr><w:b/><w:i/></w:rPr><w:t xml:space="preserve">${lastname}</w:t></w:r>', $splitText);
|
||||
}
|
||||
|
||||
public function testFindXmlBlockStart()
|
||||
{
|
||||
$toFind = '<w:r>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||
<w:lang w:val="en-GB"/>
|
||||
</w:rPr>
|
||||
<w:t>This whole paragraph will be replaced with my ${title}</w:t>
|
||||
</w:r>';
|
||||
$mainPart = '<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid wp14">
|
||||
<w:p w14:paraId="165D45AF" w14:textId="7FEC9B41" w:rsidR="005B1098" w:rsidRDefault="005B1098">
|
||||
<w:r w:rsidR="00A045B2">
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||
<w:lang w:val="en-GB"/>
|
||||
</w:rPr>
|
||||
<w:t xml:space="preserve"> ${value1} ${value2}</w:t>
|
||||
</w:r>
|
||||
<w:r>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||
<w:lang w:val="en-GB"/>
|
||||
</w:rPr>
|
||||
<w:t>.</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
<w:p w14:paraId="330D1954" w14:textId="0AB1D347" w:rsidR="00156568" w:rsidRDefault="00156568">
|
||||
<w:pPr>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||
<w:lang w:val="en-GB"/>
|
||||
</w:rPr>
|
||||
</w:pPr>
|
||||
' . $toFind . '
|
||||
</w:p>
|
||||
</w:document>';
|
||||
|
||||
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||
$position = $templateProcessor->findContainingXmlBlockForMacro('${title}', 'w:r');
|
||||
|
||||
$this->assertEquals($toFind, $templateProcessor->getSlice($position['start'], $position['end']));
|
||||
}
|
||||
|
||||
public function testShouldReturnFalseIfXmlBlockNotFound()
|
||||
{
|
||||
$mainPart = '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:p>
|
||||
<w:r>
|
||||
<w:rPr>
|
||||
<w:lang w:val="en-GB"/>
|
||||
</w:rPr>
|
||||
<w:t xml:space="preserve">this is my text containing a ${macro}</w:t>
|
||||
</w:r>
|
||||
</w:p>
|
||||
</w:document>';
|
||||
$templateProcessor = new TestableTemplateProcesor($mainPart);
|
||||
|
||||
//non-existing macro
|
||||
$result = $templateProcessor->findContainingXmlBlockForMacro('${fake-macro}', 'w:p');
|
||||
$this->assertFalse($result);
|
||||
|
||||
//existing macro but not inside node looked for
|
||||
$result = $templateProcessor->findContainingXmlBlockForMacro('${macro}', 'w:fake-node');
|
||||
$this->assertFalse($result);
|
||||
|
||||
//existing macro but end tag not found after macro
|
||||
$result = $templateProcessor->findContainingXmlBlockForMacro('${macro}', 'w:rPr');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testShouldMakeFieldsUpdateOnOpen()
|
||||
{
|
||||
$settingsPart = '<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:zoom w:percent="100"/>
|
||||
</w:settings>';
|
||||
$templateProcessor = new TestableTemplateProcesor(null, $settingsPart);
|
||||
|
||||
$templateProcessor->setUpdateFields(true);
|
||||
$this->assertContains('<w:updateFields w:val="true"/>', $templateProcessor->getSettingsPart());
|
||||
|
||||
$templateProcessor->setUpdateFields(false);
|
||||
$this->assertContains('<w:updateFields w:val="false"/>', $templateProcessor->getSettingsPart());
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,10 +86,10 @@ class ElementTest extends \PHPUnit\Framework\TestCase
|
||||
$section = $phpWord->addSection();
|
||||
$table = $section->addTable();
|
||||
$row1 = $table->addRow();
|
||||
$cell11 = $row1->addCell(1000, array('gridSpan' => 2));
|
||||
$cell11 = $row1->addCell(1000, array('gridSpan' => 2, 'bgColor' => '6086B8'));
|
||||
$cell11->addText('cell spanning 2 bellow');
|
||||
$row2 = $table->addRow();
|
||||
$cell21 = $row2->addCell(500);
|
||||
$cell21 = $row2->addCell(500, array('bgColor' => 'ffffff'));
|
||||
$cell21->addText('first cell');
|
||||
$cell22 = $row2->addCell(500);
|
||||
$cell22->addText('second cell');
|
||||
@ -100,6 +100,11 @@ class ElementTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals(1, $xpath->query('/html/body/table/tr[1]/td')->length);
|
||||
$this->assertEquals('2', $xpath->query('/html/body/table/tr/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent);
|
||||
$this->assertEquals(2, $xpath->query('/html/body/table/tr[2]/td')->length);
|
||||
|
||||
$this->assertEquals('#6086B8', $xpath->query('/html/body/table/tr[1]/td')->item(0)->attributes->getNamedItem('bgcolor')->textContent);
|
||||
$this->assertEquals('#ffffff', $xpath->query('/html/body/table/tr[1]/td')->item(0)->attributes->getNamedItem('color')->textContent);
|
||||
$this->assertEquals('#ffffff', $xpath->query('/html/body/table/tr[2]/td')->item(0)->attributes->getNamedItem('bgcolor')->textContent);
|
||||
$this->assertNull($xpath->query('/html/body/table/tr[2]/td')->item(0)->attributes->getNamedItem('color'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Writer;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
use PhpOffice\PhpWord\PhpWord;
|
||||
use PhpOffice\PhpWord\Settings;
|
||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
@ -26,7 +27,7 @@ use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class HTMLTest extends \PHPUnit\Framework\TestCase
|
||||
class HTMLTest extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* Construct
|
||||
@ -57,7 +58,7 @@ class HTMLTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
$localImage = __DIR__ . '/../_files/images/PhpWord.png';
|
||||
$archiveImage = 'zip://' . __DIR__ . '/../_files/documents/reader.docx#word/media/image1.jpeg';
|
||||
$gdImage = 'http://php.net/images/logos/php-med-trans-light.gif';
|
||||
$gdImage = self::getRemoteGifImageUrl();
|
||||
$objectSrc = __DIR__ . '/../_files/documents/sheet.xls';
|
||||
$file = __DIR__ . '/../_files/temp.html';
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase
|
||||
*/
|
||||
public function testUnmatchedElements()
|
||||
{
|
||||
$elements = array('Container', 'Text', 'Title', 'Link', 'Image', 'Table');
|
||||
$elements = array('Container', 'Text', 'Title', 'Link', 'Image', 'Table', 'Field');
|
||||
foreach ($elements as $element) {
|
||||
$objectClass = 'PhpOffice\\PhpWord\\Writer\\RTF\\Element\\' . $element;
|
||||
$parentWriter = new RTF();
|
||||
@ -39,4 +39,40 @@ class ElementTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals('', $object->write());
|
||||
}
|
||||
}
|
||||
|
||||
public function testPageField()
|
||||
{
|
||||
$parentWriter = new RTF();
|
||||
$element = new \PhpOffice\PhpWord\Element\Field('PAGE');
|
||||
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
|
||||
|
||||
$this->assertEquals("{\\field{\\*\\fldinst PAGE}{\\fldrslt}}\\par\n", $field->write());
|
||||
}
|
||||
|
||||
public function testNumpageField()
|
||||
{
|
||||
$parentWriter = new RTF();
|
||||
$element = new \PhpOffice\PhpWord\Element\Field('NUMPAGES');
|
||||
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
|
||||
|
||||
$this->assertEquals("{\\field{\\*\\fldinst NUMPAGES}{\\fldrslt}}\\par\n", $field->write());
|
||||
}
|
||||
|
||||
public function testDateField()
|
||||
{
|
||||
$parentWriter = new RTF();
|
||||
$element = new \PhpOffice\PhpWord\Element\Field('DATE', array('dateformat' => 'd MM yyyy H:mm:ss'));
|
||||
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
|
||||
|
||||
$this->assertEquals("{\\field{\\*\\fldinst DATE \\\\@ \"d MM yyyy H:mm:ss\"}{\\fldrslt}}\\par\n", $field->write());
|
||||
}
|
||||
|
||||
public function testIndexField()
|
||||
{
|
||||
$parentWriter = new RTF();
|
||||
$element = new \PhpOffice\PhpWord\Element\Field('INDEX');
|
||||
$field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element);
|
||||
|
||||
$this->assertEquals("{}\\par\n", $field->write());
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,4 +510,25 @@ class ElementTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:t'));
|
||||
$this->assertEquals('this text contains an & (ampersant)', $doc->getElement('/w:document/w:body/w:p/w:r/w:t')->nodeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ListItemRun paragraph style writing
|
||||
*/
|
||||
public function testListItemRunStyleWriting()
|
||||
{
|
||||
$phpWord = new PhpWord();
|
||||
$phpWord->addParagraphStyle('MyParagraphStyle', array('spaceBefore' => 400));
|
||||
|
||||
$section = $phpWord->addSection();
|
||||
$listItemRun = $section->addListItemRun(0, null, 'MyParagraphStyle');
|
||||
$listItemRun->addText('List item');
|
||||
$listItemRun->addText(' in bold', array('bold' => true));
|
||||
|
||||
$doc = TestHelperDOCX::getDocument($phpWord);
|
||||
$this->assertFalse($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:pPr'));
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:pPr/w:pStyle'));
|
||||
$this->assertEquals('List item', $doc->getElement('/w:document/w:body/w:p/w:r[1]/w:t')->nodeValue);
|
||||
$this->assertEquals(' in bold', $doc->getElement('/w:document/w:body/w:p/w:r[2]/w:t')->nodeValue);
|
||||
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r[2]/w:rPr/w:b'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,4 +141,21 @@ class TableTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertSame($value, (int) $doc->getElementAttribute($path, 'w:w'));
|
||||
$this->assertSame($type, $doc->getElementAttribute($path, 'w:type'));
|
||||
}
|
||||
|
||||
public function testRigthToLeft()
|
||||
{
|
||||
$tableStyle = new Table();
|
||||
$tableStyle->setBidiVisual(true);
|
||||
|
||||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$table = $section->addTable($tableStyle);
|
||||
$table->addRow();
|
||||
|
||||
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
|
||||
|
||||
$path = '/w:document/w:body/w:tbl/w:tblPr/w:bidiVisual';
|
||||
$this->assertTrue($doc->elementExists($path));
|
||||
$this->assertEquals('1', $doc->getElementAttribute($path, 'w:val'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
namespace PhpOffice\PhpWord\Writer;
|
||||
|
||||
use PhpOffice\PhpWord\AbstractWebServerEmbeddedTest;
|
||||
use PhpOffice\PhpWord\PhpWord;
|
||||
use PhpOffice\PhpWord\SimpleType\Jc;
|
||||
use PhpOffice\PhpWord\TestHelperDOCX;
|
||||
@ -26,7 +27,7 @@ use PhpOffice\PhpWord\TestHelperDOCX;
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
*/
|
||||
class Word2007Test extends \PHPUnit\Framework\TestCase
|
||||
class Word2007Test extends AbstractWebServerEmbeddedTest
|
||||
{
|
||||
/**
|
||||
* Tear down after each test
|
||||
@ -75,7 +76,7 @@ class Word2007Test extends \PHPUnit\Framework\TestCase
|
||||
public function testSave()
|
||||
{
|
||||
$localImage = __DIR__ . '/../_files/images/earth.jpg';
|
||||
$remoteImage = 'http://php.net/images/logos/new-php-logo.png';
|
||||
$remoteImage = self::getRemoteGifImageUrl();
|
||||
$phpWord = new PhpWord();
|
||||
$phpWord->addFontStyle('Font', array('size' => 11));
|
||||
$phpWord->addParagraphStyle('Paragraph', array('alignment' => Jc::CENTER));
|
||||
|
||||
BIN
tests/PhpWord/_files/images/new-php-logo.png
Normal file
BIN
tests/PhpWord/_files/images/new-php-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
80
tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php
Normal file
80
tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private static $httpServer;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
if (self::isBuiltinServerSupported()) {
|
||||
self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files');
|
||||
self::$httpServer->start();
|
||||
while (!self::$httpServer->isRunning()) {
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (self::isBuiltinServerSupported()) {
|
||||
self::$httpServer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
protected static function getBaseUrl()
|
||||
{
|
||||
return 'http://localhost:8080';
|
||||
}
|
||||
|
||||
protected static function getRemoteImageUrl()
|
||||
{
|
||||
if (self::$httpServer) {
|
||||
return self::getBaseUrl() . '/images/new-php-logo.png';
|
||||
}
|
||||
|
||||
return 'http://php.net/images/logos/new-php-logo.png';
|
||||
}
|
||||
|
||||
protected static function getRemoteGifImageUrl()
|
||||
{
|
||||
if (self::$httpServer) {
|
||||
return self::getBaseUrl() . '/images/mario.gif';
|
||||
}
|
||||
|
||||
return 'http://php.net/images/logos/php-med-trans-light.gif';
|
||||
}
|
||||
|
||||
protected static function getRemoteBmpImageUrl()
|
||||
{
|
||||
if (self::$httpServer) {
|
||||
return self::getBaseUrl() . '/images/duke_nukem.bmp';
|
||||
}
|
||||
|
||||
return 'https://samples.libav.org/image-samples/RACECAR.BMP';
|
||||
}
|
||||
|
||||
private static function isBuiltinServerSupported()
|
||||
{
|
||||
return version_compare(PHP_VERSION, '5.4.0', '>=');
|
||||
}
|
||||
}
|
||||
@ -25,9 +25,10 @@ namespace PhpOffice\PhpWord;
|
||||
*/
|
||||
class TestableTemplateProcesor extends TemplateProcessor
|
||||
{
|
||||
public function __construct($mainPart = null)
|
||||
public function __construct($mainPart = null, $settingsPart = null)
|
||||
{
|
||||
$this->tempDocumentMainPart = $mainPart;
|
||||
$this->tempDocumentSettingsPart = $settingsPart;
|
||||
}
|
||||
|
||||
public function fixBrokenMacros($documentPart)
|
||||
@ -35,6 +36,16 @@ class TestableTemplateProcesor extends TemplateProcessor
|
||||
return parent::fixBrokenMacros($documentPart);
|
||||
}
|
||||
|
||||
public function splitTextIntoTexts($text)
|
||||
{
|
||||
return parent::splitTextIntoTexts($text);
|
||||
}
|
||||
|
||||
public function textNeedsSplitting($text)
|
||||
{
|
||||
return parent::textNeedsSplitting($text);
|
||||
}
|
||||
|
||||
public function getVariablesForPart($documentPartXML)
|
||||
{
|
||||
$documentPartXML = parent::fixBrokenMacros($documentPartXML);
|
||||
@ -42,8 +53,34 @@ class TestableTemplateProcesor extends TemplateProcessor
|
||||
return parent::getVariablesForPart($documentPartXML);
|
||||
}
|
||||
|
||||
public function findXmlBlockStart($offset, $blockType)
|
||||
{
|
||||
return parent::findXmlBlockStart($offset, $blockType);
|
||||
}
|
||||
|
||||
public function findContainingXmlBlockForMacro($macro, $blockType = 'w:p')
|
||||
{
|
||||
return parent::findContainingXmlBlockForMacro($macro, $blockType);
|
||||
}
|
||||
|
||||
public function getSlice($startPosition, $endPosition = 0)
|
||||
{
|
||||
return parent::getSlice($startPosition, $endPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMainPart()
|
||||
{
|
||||
return $this->tempDocumentMainPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSettingsPart()
|
||||
{
|
||||
return $this->tempDocumentSettingsPart;
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,10 +76,10 @@ class XmlDocument
|
||||
$this->file = $file;
|
||||
|
||||
$file = $this->path . '/' . $file;
|
||||
libxml_disable_entity_loader(false);
|
||||
$orignalLibEntityLoader = libxml_disable_entity_loader(false);
|
||||
$this->dom = new \DOMDocument();
|
||||
$this->dom->load($file);
|
||||
libxml_disable_entity_loader(true);
|
||||
libxml_disable_entity_loader($orignalLibEntityLoader);
|
||||
|
||||
return $this->dom;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user