Merge branch 'master' into develop_v1.0

Conflicts:
	.travis.yml
	composer.json
This commit is contained in:
troosan 2019-10-01 23:55:29 +02:00
commit 847520f380
65 changed files with 1525 additions and 176 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View 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:

View 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
View 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
View File

@ -13,7 +13,6 @@ composer.phar
vendor
/report
/build
/samples/resources
/samples/results
/.settings
phpword.ini

View File

@ -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

View File

@ -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)

View File

@ -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!

View File

@ -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.

View File

@ -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"
}
}

View File

@ -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

View File

@ -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

View File

@ -34,7 +34,7 @@ Example:
{
"require": {
"phpoffice/phpword": "v0.14.*"
"phpoffice/phpword": "v0.17.*"
}
}

View File

@ -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:

View File

@ -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);

View File

@ -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>

View File

@ -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(

View File

@ -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');

View File

@ -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>';

View File

@ -14,6 +14,29 @@ $textrun->addText('This is a Left to Right paragraph.');
$textrun = $section->addTextRun(array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END));
$textrun->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) {

View 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';
}

View File

@ -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>';

View File

@ -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')),
);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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'),

View File

@ -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) {

View 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';
}

View File

@ -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;
}

View File

@ -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');
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View 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;
}
}

View File

@ -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
}
}

View File

@ -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),

View File

@ -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);

View File

@ -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');
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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));

View File

@ -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());
}
}

View File

@ -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'));
}
}

View File

@ -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,

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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'));
}
/**

View File

@ -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';

View File

@ -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());
}
}

View File

@ -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'));
}
}

View File

@ -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'));
}
}

View File

@ -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));

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View 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', '>=');
}
}

View File

@ -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;
}
}

View File

@ -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;
}