Compare commits

..

No commits in common. "master" and "1.0.0" have entirely different histories.

32 changed files with 322 additions and 1276 deletions

4
.gitattributes vendored
View File

@ -1,6 +1,6 @@
# build config # build config
/.scrutinizer.yml export-ignore /.scrutinizer.yml export-ignore
/.github export-ignore /.travis.yml export-ignore
/php_cs.dist export-ignore /php_cs.dist export-ignore
/phpmd.xml.dist export-ignore /phpmd.xml.dist export-ignore
/phpstan.neon export-ignore /phpstan.neon export-ignore
@ -18,4 +18,4 @@
# tests # tests
/phpunit.xml.dist export-ignore /phpunit.xml.dist export-ignore
/tests export-ignore /tests export-ignore

View File

@ -9,6 +9,6 @@ labels: WontFix
Documentation is available on [Read the Docs](https://phpword.readthedocs.io/en/latest/). 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/master/samples). 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). Usage questions belong on [Stack Overflow](https://stackoverflow.com/questions/tagged/phpword).

View File

@ -19,7 +19,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@ -33,7 +33,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)" run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies - name: Cache composer dependencies
uses: actions/cache@v3 uses: actions/cache@v2
with: with:
path: ${{ steps.composer-cache.outputs.dir }} path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -58,7 +58,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@ -73,37 +73,40 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)" run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies - name: Cache composer dependencies
uses: actions/cache@v3 uses: actions/cache@v2
with: with:
path: ${{ steps.composer-cache.outputs.dir }} path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer- restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies - name: Composer Install
run: composer install --no-progress --prefer-dist --optimize-autoloader run: composer global require friendsofphp/php-cs-fixer
- name: Add environment path
run: export PATH="$PATH:$HOME/.composer/vendor/bin"
- name: Code style with PHP-CS-Fixer - name: Code style with PHP-CS-Fixer
run: ./vendor/bin/php-cs-fixer fix --dry-run --diff run: php-cs-fixer fix --dry-run --diff
coverage: coverage:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 7.4 php-version: 7.4
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
coverage: pcov coverage: xdebug
- name: Get composer cache directory - name: Get composer cache directory
id: composer-cache id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)" run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies - name: Cache composer dependencies
uses: actions/cache@v3 uses: actions/cache@v2
with: with:
path: ${{ steps.composer-cache.outputs.dir }} path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions - name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2

32
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
days-before-stale: 90
days-before-close: 60
exempt-issue-labels: 'pinned,security'
exempt-pr-labels: 'pinned,security'
stale-issue-message: 'This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
If this is still an issue for you, please try to help by debugging it
further and sharing your results.
Thank you for your contributions.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
If this is still an issue for you, please try to complete the PR by adding tests and making sure that the CI is green.
Thank you for your contributions.'

View File

@ -1,16 +1,23 @@
# ![PHPWord](https://rawgit.com/PHPOffice/PHPWord/develop/docs/images/phpword.svg "PHPWord") # ![PHPWord](https://rawgit.com/PHPOffice/PHPWord/develop/docs/images/phpword.svg "PHPWord")
Master:
[![Latest Stable Version](https://poser.pugx.org/phpoffice/phpword/v/stable.png)](https://packagist.org/packages/phpoffice/phpword) [![Latest Stable Version](https://poser.pugx.org/phpoffice/phpword/v/stable.png)](https://packagist.org/packages/phpoffice/phpword)
[![CI](https://github.com/PHPOffice/PHPWord/actions/workflows/ci.yml/badge.svg)](https://github.com/PHPOffice/PHPWord/actions/workflows/ci.yml) [![Build Status](https://travis-ci.org/PHPOffice/PHPWord.svg?branch=master)](https://travis-ci.org/PHPOffice/PHPWord)
[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/) [![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/)
[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/) [![Coverage Status](https://coveralls.io/repos/github/PHPOffice/PHPWord/badge.svg?branch=master)](https://coveralls.io/github/PHPOffice/PHPWord?branch=master)
[![Total Downloads](https://poser.pugx.org/phpoffice/phpword/downloads.png)](https://packagist.org/packages/phpoffice/phpword) [![Total Downloads](https://poser.pugx.org/phpoffice/phpword/downloads.png)](https://packagist.org/packages/phpoffice/phpword)
[![License](https://poser.pugx.org/phpoffice/phpword/license.png)](https://packagist.org/packages/phpoffice/phpword) [![License](https://poser.pugx.org/phpoffice/phpword/license.png)](https://packagist.org/packages/phpoffice/phpword)
[![Join the chat at https://gitter.im/PHPOffice/PHPWord](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPWord) [![Join the chat at https://gitter.im/PHPOffice/PHPWord](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPWord)
Develop:
[![Latest Development Version](https://img.shields.io/badge/unstable-dev--develop-orange.svg)](https://packagist.org/packages/phpoffice/phpword#dev-develop)
[![Build Status](https://travis-ci.org/PHPOffice/PHPWord.svg?branch=develop)](https://travis-ci.org/PHPOffice/PHPWord/branches)
[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/PHPOffice/PHPWord/?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/PHPOffice/PHPWord/badge.svg?branch=develop)](https://coveralls.io/github/PHPOffice/PHPWord?branch=develop)
PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF. PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF.
PHPWord is an open source project licensed under the terms of [LGPL version 3](COPYING.LESSER). PHPWord is aimed to be a high quality software product by incorporating [continuous integration](https://github.com/PHPOffice/PHPWord/actions) and unit testing. 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) If you have any questions, please ask on [StackOverFlow](https://stackoverflow.com/questions/tagged/phpword)
@ -71,9 +78,9 @@ Run the following to use the latest stable version
```sh ```sh
composer require phpoffice/phpword composer require phpoffice/phpword
``` ```
or if you want the latest unreleased version or if you want the latest develop version
```sh ```sh
composer require phpoffice/phpword:dev-master composer require phpoffice/phpword:dev-develop
``` ```
## Getting started ## Getting started
@ -158,6 +165,6 @@ 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. We welcome everyone to contribute to PHPWord. Below are some of the things that you can do to contribute.
- Read [our contributing guide](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 [master](https://github.com/PHPOffice/PHPWord/tree/master) branch. - [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. - 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. - Follow [@PHPWord](https://twitter.com/PHPWord) and [@PHPOffice](https://twitter.com/PHPOffice) on Twitter.

View File

@ -70,11 +70,11 @@
"ext-libxml": "*", "ext-libxml": "*",
"dompdf/dompdf": "^2.0", "dompdf/dompdf": "^2.0",
"mpdf/mpdf": "^8.1", "mpdf/mpdf": "^8.1",
"php-coveralls/php-coveralls": "^2.5",
"phpmd/phpmd": "^2.13", "phpmd/phpmd": "^2.13",
"phpunit/phpunit": ">=7.0", "phpunit/phpunit": ">=7.0",
"tecnickcom/tcpdf": "^6.5", "tecnickcom/tcpdf": "^6.5",
"symfony/process": "^4.4", "symfony/process": "^4.4"
"friendsofphp/php-cs-fixer": "^3.3"
}, },
"suggest": { "suggest": {
"ext-zip": "Allows writing OOXML and ODF", "ext-zip": "Allows writing OOXML and ODF",
@ -92,5 +92,10 @@
"psr-4": { "psr-4": {
"PhpOffice\\PhpWordTests\\": "tests/PhpWordTests" "PhpOffice\\PhpWordTests\\": "tests/PhpWordTests"
} }
},
"extra": {
"branch-alias": {
"dev-develop": "0.19-dev"
}
} }
} }

View File

@ -192,11 +192,11 @@ You can also specify the status of the spell and grammar checks, marking spellin
.. code-block:: php .. code-block:: php
$proofState = new \PhpOffice\PhpWord\ComplexType\ProofState(); $proofState = new ProofState();
$proofState->setGrammar(\PhpOffice\PhpWord\ComplexType\ProofState::CLEAN); $proofState->setGrammar(ProofState::CLEAN);
$proofState->setSpelling(\PhpOffice\PhpWord\ComplexType\ProofState::DIRTY); $proofState->setSpelling(ProofState::DIRTY);
$phpWord->getSettings()->setProofState($proofState); $phpWord->getSettings()->setProofState(proofState);
Track Revisions Track Revisions
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~

View File

@ -39,7 +39,7 @@ Example:
.. code-block:: bash .. code-block:: bash
composer require phpoffice/phpword:dev-master composer require phpoffice/phpword:dev-develop
Using samples Using samples
------------- -------------

View File

@ -13,10 +13,13 @@ Applications <http://en.wikipedia.org/wiki/OpenDocument>`__
Format <http://en.wikipedia.org/wiki/Rich_Text_Format>`__ (RTF). Format <http://en.wikipedia.org/wiki/Rich_Text_Format>`__ (RTF).
PHPWord is an open source project licensed under the terms of `LGPL PHPWord is an open source project licensed under the terms of `LGPL
version 3 <https://github.com/PHPOffice/PHPWord/blob/master/COPYING.LESSER>`__. version 3 <https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER>`__.
PHPWord is aimed to be a high quality software product. 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 this Developers' You can learn more about PHPWord by reading this Developers'
Documentation. Documentation and the `API
Documentation <http://phpoffice.github.io/PHPWord/docs/develop/>`__.
Features Features
-------- --------
@ -188,7 +191,7 @@ things that you can do to contribute.
guide <https://github.com/PHPOffice/PHPWord/blob/master/CONTRIBUTING.md>`__. guide <https://github.com/PHPOffice/PHPWord/blob/master/CONTRIBUTING.md>`__.
- `Fork us <https://github.com/PHPOffice/PHPWord/fork>`__ and `request - `Fork us <https://github.com/PHPOffice/PHPWord/fork>`__ and `request
a pull <https://github.com/PHPOffice/PHPWord/pulls>`__ to the a pull <https://github.com/PHPOffice/PHPWord/pulls>`__ to the
`master <https://github.com/PHPOffice/PHPWord/tree/master>`__ `develop <https://github.com/PHPOffice/PHPWord/tree/develop>`__
branch. branch.
- Submit `bug reports or feature - Submit `bug reports or feature
requests <https://github.com/PHPOffice/PHPWord/issues>`__ to GitHub. requests <https://github.com/PHPOffice/PHPWord/issues>`__ to GitHub.

View File

@ -29,7 +29,7 @@ Use ``php://output`` as the filename.
.. code-block:: php .. code-block:: php
$phpWord = new \PhpOffice\PhpWord\PhpWord(); $phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection(); $section = $phpWord->createSection();
$section->addText('Hello World!'); $section->addText('Hello World!');
$file = 'HelloWorld.docx'; $file = 'HelloWorld.docx';
header("Content-Description: File Transfer"); header("Content-Description: File Transfer");

View File

@ -4,7 +4,7 @@ Templates processing
==================== ====================
You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced. You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced.
By default Macros are defined like this: ``${search-pattern}`` but you can define custom macros. Macros are defined like this: ``${search-pattern}``.
To load a template file, create a new instance of the TemplateProcessor. To load a template file, create a new instance of the TemplateProcessor.
.. code-block:: php .. code-block:: php
@ -35,30 +35,6 @@ You can also set multiple values by passing all of them in an array.
$templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe')); $templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe'));
setMacroOpeningChars
""""""""
You can define a custom opening macro. The following will set ``{#`` as the opening search pattern.
.. code-block:: php
$templateProcessor->setMacroOpeningChars('{#');
setMacroClosingChars
""""""""
You can define a custom closing macro. The following will set ``#}`` as the closing search pattern.
.. code-block:: php
$templateProcessor->setMacroClosingChars('#}');
setMacroChars
""""""""
You can define a custom opening and closing macro at the same time . The following will set the search-pattern like this: ``{#search-pattern#}`` .
.. code-block:: php
$templateProcessor->setMacroChars('{#', '#}');
setImageValue setImageValue
""""""""""""" """""""""""""
The search-pattern model for images can be like: The search-pattern model for images can be like:

View File

@ -323,8 +323,6 @@ abstract class AbstractPart
$element->setChangeInfo($type, $author, $date); $element->setChangeInfo($type, $author, $date);
} }
} }
} elseif ($node->nodeName == 'w:softHyphen') {
$element = $parent->addText("\u{200c}", $fontStyle, $paragraphStyle);
} }
} }
@ -560,7 +558,7 @@ abstract class AbstractPart
'valign' => [self::READ_VALUE, 'w:vAlign'], 'valign' => [self::READ_VALUE, 'w:vAlign'],
'textDirection' => [self::READ_VALUE, 'w:textDirection'], 'textDirection' => [self::READ_VALUE, 'w:textDirection'],
'gridSpan' => [self::READ_VALUE, 'w:gridSpan'], 'gridSpan' => [self::READ_VALUE, 'w:gridSpan'],
'vMerge' => [self::READ_VALUE, 'w:vMerge', null, null, 'continue'], 'vMerge' => [self::READ_VALUE, 'w:vMerge'],
'bgColor' => [self::READ_VALUE, 'w:shd', 'w:fill'], 'bgColor' => [self::READ_VALUE, 'w:shd', 'w:fill'],
]; ];
@ -628,7 +626,7 @@ abstract class AbstractPart
$styles = []; $styles = [];
foreach ($styleDefs as $styleProp => $styleVal) { foreach ($styleDefs as $styleProp => $styleVal) {
[$method, $element, $attribute, $expected, $default] = array_pad($styleVal, 5, null); [$method, $element, $attribute, $expected] = array_pad($styleVal, 4, null);
$element = $this->findPossibleElement($xmlReader, $parentNode, $element); $element = $this->findPossibleElement($xmlReader, $parentNode, $element);
if ($element === null) { if ($element === null) {
@ -642,7 +640,7 @@ abstract class AbstractPart
// Use w:val as default if no attribute assigned // Use w:val as default if no attribute assigned
$attribute = ($attribute === null) ? 'w:val' : $attribute; $attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node) ?? $default; $attributeValue = $xmlReader->getAttribute($attribute, $node);
$styleValue = $this->readStyleDef($method, $attributeValue, $expected); $styleValue = $this->readStyleDef($method, $attributeValue, $expected);
if ($styleValue !== null) { if ($styleValue !== null) {

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -27,46 +29,48 @@ class Settings
* *
* @const string * @const string
*/ */
public const ZIPARCHIVE = 'ZipArchive'; const ZIPARCHIVE = 'ZipArchive';
public const PCLZIP = 'PclZip'; const PCLZIP = 'PclZip';
public const OLD_LIB = \PhpOffice\PhpWord\Shared\ZipArchive::class; // @deprecated 0.11 const OLD_LIB = \PhpOffice\PhpWord\Shared\ZipArchive::class; // @deprecated 0.11
/** /**
* PDF rendering libraries. * PDF rendering libraries.
* *
* @const string * @const string
*/ */
public const PDF_RENDERER_DOMPDF = 'DomPDF'; const PDF_RENDERER_DOMPDF = 'DomPDF';
public const PDF_RENDERER_TCPDF = 'TCPDF'; const PDF_RENDERER_TCPDF = 'TCPDF';
public const PDF_RENDERER_MPDF = 'MPDF'; const PDF_RENDERER_MPDF = 'MPDF';
/** /**
* Measurement units multiplication factor. * Measurement units multiplication factor.
*
* Applied to: * Applied to:
* - Section: margins, header/footer height, gutter, column spacing * - Section: margins, header/footer height, gutter, column spacing
* - Tab: position * - Tab: position
* - Indentation: left, right, firstLine, hanging * - Indentation: left, right, firstLine, hanging
* - Spacing: before, after. * - Spacing: before, after
* *
* @const string * @const string
*/ */
public const UNIT_TWIP = 'twip'; // = 1/20 point const UNIT_TWIP = 'twip'; // = 1/20 point
public const UNIT_CM = 'cm'; const UNIT_CM = 'cm';
public const UNIT_MM = 'mm'; const UNIT_MM = 'mm';
public const UNIT_INCH = 'inch'; const UNIT_INCH = 'inch';
public const UNIT_POINT = 'point'; // = 1/72 inch const UNIT_POINT = 'point'; // = 1/72 inch
public const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points
/** /**
* Default font settings. * Default font settings.
*
* OOXML defined font size values in halfpoints, i.e. twice of what PhpWord * OOXML defined font size values in halfpoints, i.e. twice of what PhpWord
* use, and the conversion will be conducted during XML writing. * use, and the conversion will be conducted during XML writing.
*/ */
public const DEFAULT_FONT_NAME = 'Arial'; const DEFAULT_FONT_NAME = 'Arial';
public const DEFAULT_FONT_SIZE = 10; const DEFAULT_FONT_SIZE = 10;
public const DEFAULT_FONT_COLOR = '000000'; const DEFAULT_FONT_COLOR = '000000';
public const DEFAULT_FONT_CONTENT_TYPE = 'default'; // default|eastAsia|cs const DEFAULT_FONT_CONTENT_TYPE = 'default'; // default|eastAsia|cs
public const DEFAULT_PAPER = 'A4'; const DEFAULT_PAPER = 'A4';
/** /**
* Compatibility option for XMLWriter. * Compatibility option for XMLWriter.
@ -85,21 +89,21 @@ class Settings
/** /**
* Name of the external Library used for rendering PDF files. * Name of the external Library used for rendering PDF files.
* *
* @var null|string * @var string
*/ */
private static $pdfRendererName; private static $pdfRendererName;
/** /**
* Directory Path to the external Library used for rendering PDF files. * Directory Path to the external Library used for rendering PDF files.
* *
* @var null|string * @var string
*/ */
private static $pdfRendererPath; private static $pdfRendererPath;
/** /**
* Measurement unit. * Measurement unit.
* *
* @var string * @var float|int
*/ */
private static $measurementUnit = self::UNIT_TWIP; private static $measurementUnit = self::UNIT_TWIP;
@ -113,7 +117,7 @@ class Settings
/** /**
* Default font size. * Default font size.
* *
* @var float|int * @var int
*/ */
private static $defaultFontSize = self::DEFAULT_FONT_SIZE; private static $defaultFontSize = self::DEFAULT_FONT_SIZE;
@ -144,17 +148,23 @@ class Settings
* *
* @return bool Compatibility * @return bool Compatibility
*/ */
public static function hasCompatibility(): bool public static function hasCompatibility()
{ {
return self::$xmlWriterCompatibility; return self::$xmlWriterCompatibility;
} }
/** /**
* Set the compatibility option used by the XMLWriter. * Set the compatibility option used by the XMLWriter.
* This sets the setIndent and setIndentString for better compatibility. *
* This sets the setIndent and setIndentString for better compatibility
*
* @param bool $compatibility
*
* @return bool
*/ */
public static function setCompatibility(bool $compatibility): bool public static function setCompatibility($compatibility)
{ {
$compatibility = (bool) $compatibility;
self::$xmlWriterCompatibility = $compatibility; self::$xmlWriterCompatibility = $compatibility;
return true; return true;
@ -162,16 +172,22 @@ class Settings
/** /**
* Get zip handler class. * Get zip handler class.
*
* @return string
*/ */
public static function getZipClass(): string public static function getZipClass()
{ {
return self::$zipClass; return self::$zipClass;
} }
/** /**
* Set zip handler class. * Set zip handler class.
*
* @param string $zipClass
*
* @return bool
*/ */
public static function setZipClass(string $zipClass): bool public static function setZipClass($zipClass)
{ {
if (in_array($zipClass, [self::PCLZIP, self::ZIPARCHIVE, self::OLD_LIB])) { if (in_array($zipClass, [self::PCLZIP, self::ZIPARCHIVE, self::OLD_LIB])) {
self::$zipClass = $zipClass; self::$zipClass = $zipClass;
@ -185,9 +201,12 @@ class Settings
/** /**
* Set details of the external library for rendering PDF files. * Set details of the external library for rendering PDF files.
* *
* @param string $libraryName
* @param string $libraryBaseDir
*
* @return bool Success or failure * @return bool Success or failure
*/ */
public static function setPdfRenderer(string $libraryName, string $libraryBaseDir): bool public static function setPdfRenderer($libraryName, $libraryBaseDir)
{ {
if (!self::setPdfRendererName($libraryName)) { if (!self::setPdfRendererName($libraryName)) {
return false; return false;
@ -198,16 +217,22 @@ class Settings
/** /**
* Return the PDF Rendering Library. * Return the PDF Rendering Library.
*
* @return string
*/ */
public static function getPdfRendererName(): ?string public static function getPdfRendererName()
{ {
return self::$pdfRendererName; return self::$pdfRendererName;
} }
/** /**
* Identify the external library to use for rendering PDF files. * Identify the external library to use for rendering PDF files.
*
* @param string $libraryName
*
* @return bool
*/ */
public static function setPdfRendererName(?string $libraryName): bool public static function setPdfRendererName($libraryName)
{ {
$pdfRenderers = [self::PDF_RENDERER_DOMPDF, self::PDF_RENDERER_TCPDF, self::PDF_RENDERER_MPDF]; $pdfRenderers = [self::PDF_RENDERER_DOMPDF, self::PDF_RENDERER_TCPDF, self::PDF_RENDERER_MPDF];
if (!in_array($libraryName, $pdfRenderers)) { if (!in_array($libraryName, $pdfRenderers)) {
@ -220,8 +245,10 @@ class Settings
/** /**
* Return the directory path to the PDF Rendering Library. * Return the directory path to the PDF Rendering Library.
*
* @return string
*/ */
public static function getPdfRendererPath(): ?string public static function getPdfRendererPath()
{ {
return self::$pdfRendererPath; return self::$pdfRendererPath;
} }
@ -229,11 +256,11 @@ class Settings
/** /**
* Location of external library to use for rendering PDF files. * Location of external library to use for rendering PDF files.
* *
* @param null|string $libraryBaseDir Directory path to the library's base folder * @param string $libraryBaseDir Directory path to the library's base folder
* *
* @return bool Success or failure * @return bool Success or failure
*/ */
public static function setPdfRendererPath(?string $libraryBaseDir): bool public static function setPdfRendererPath($libraryBaseDir)
{ {
if (!$libraryBaseDir || false === file_exists($libraryBaseDir) || false === is_readable($libraryBaseDir)) { if (!$libraryBaseDir || false === file_exists($libraryBaseDir) || false === is_readable($libraryBaseDir)) {
return false; return false;
@ -245,25 +272,25 @@ class Settings
/** /**
* Get measurement unit. * Get measurement unit.
*
* @return string
*/ */
public static function getMeasurementUnit(): string public static function getMeasurementUnit()
{ {
return self::$measurementUnit; return self::$measurementUnit;
} }
/** /**
* Set measurement unit. * Set measurement unit.
*
* @param string $value
*
* @return bool
*/ */
public static function setMeasurementUnit(string $value): bool public static function setMeasurementUnit($value)
{ {
$units = [ $units = [self::UNIT_TWIP, self::UNIT_CM, self::UNIT_MM, self::UNIT_INCH,
self::UNIT_TWIP, self::UNIT_POINT, self::UNIT_PICA, ];
self::UNIT_CM,
self::UNIT_MM,
self::UNIT_INCH,
self::UNIT_POINT,
self::UNIT_PICA,
];
if (!in_array($value, $units)) { if (!in_array($value, $units)) {
return false; return false;
} }
@ -275,11 +302,11 @@ class Settings
/** /**
* Sets the user defined path to temporary directory. * Sets the user defined path to temporary directory.
* *
* @param string $tempDir The user defined path to temporary directory
*
* @since 0.12.0 * @since 0.12.0
*
* @param string $tempDir The user defined path to temporary directory
*/ */
public static function setTempDir(string $tempDir): void public static function setTempDir($tempDir): void
{ {
self::$tempDir = $tempDir; self::$tempDir = $tempDir;
} }
@ -288,8 +315,10 @@ class Settings
* Returns path to temporary directory. * Returns path to temporary directory.
* *
* @since 0.12.0 * @since 0.12.0
*
* @return string
*/ */
public static function getTempDir(): string public static function getTempDir()
{ {
if (!empty(self::$tempDir)) { if (!empty(self::$tempDir)) {
$tempDir = self::$tempDir; $tempDir = self::$tempDir;
@ -302,34 +331,44 @@ class Settings
/** /**
* @since 0.13.0 * @since 0.13.0
*
* @return bool
*/ */
public static function isOutputEscapingEnabled(): bool public static function isOutputEscapingEnabled()
{ {
return self::$outputEscapingEnabled; return self::$outputEscapingEnabled;
} }
/** /**
* @since 0.13.0 * @since 0.13.0
*
* @param bool $outputEscapingEnabled
*/ */
public static function setOutputEscapingEnabled(bool $outputEscapingEnabled): void public static function setOutputEscapingEnabled($outputEscapingEnabled): void
{ {
self::$outputEscapingEnabled = $outputEscapingEnabled; self::$outputEscapingEnabled = $outputEscapingEnabled;
} }
/** /**
* Get default font name. * Get default font name.
*
* @return string
*/ */
public static function getDefaultFontName(): string public static function getDefaultFontName()
{ {
return self::$defaultFontName; return self::$defaultFontName;
} }
/** /**
* Set default font name. * Set default font name.
*
* @param string $value
*
* @return bool
*/ */
public static function setDefaultFontName(string $value): bool public static function setDefaultFontName($value)
{ {
if (trim($value) !== '') { if (is_string($value) && trim($value) !== '') {
self::$defaultFontName = $value; self::$defaultFontName = $value;
return true; return true;
@ -341,7 +380,7 @@ class Settings
/** /**
* Get default font size. * Get default font size.
* *
* @return float|int * @return int
*/ */
public static function getDefaultFontSize() public static function getDefaultFontSize()
{ {
@ -351,11 +390,14 @@ class Settings
/** /**
* Set default font size. * Set default font size.
* *
* @param null|float|int $value * @param int $value
*
* @return bool
*/ */
public static function setDefaultFontSize($value): bool public static function setDefaultFontSize($value)
{ {
if ((is_int($value) || is_float($value)) && (int) $value > 0) { $value = (int) $value;
if ($value > 0) {
self::$defaultFontSize = $value; self::$defaultFontSize = $value;
return true; return true;
@ -366,8 +408,12 @@ class Settings
/** /**
* Load setting from phpword.yml or phpword.yml.dist. * Load setting from phpword.yml or phpword.yml.dist.
*
* @param string $filename
*
* @return array
*/ */
public static function loadConfig(?string $filename = null): array public static function loadConfig($filename = null)
{ {
// Get config file // Get config file
$configFile = null; $configFile = null;
@ -409,18 +455,24 @@ class Settings
/** /**
* Get default paper. * Get default paper.
*
* @return string
*/ */
public static function getDefaultPaper(): string public static function getDefaultPaper()
{ {
return self::$defaultPaper; return self::$defaultPaper;
} }
/** /**
* Set default paper. * Set default paper.
*
* @param string $value
*
* @return bool
*/ */
public static function setDefaultPaper(string $value): bool public static function setDefaultPaper($value)
{ {
if (trim($value) !== '') { if (is_string($value) && trim($value) !== '') {
self::$defaultPaper = $value; self::$defaultPaper = $value;
return true; return true;

View File

@ -1,80 +0,0 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @see https://github.com/PHPOffice/PHPWord
*
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
declare(strict_types=1);
namespace PhpOffice\PhpWord\Shared;
class Css
{
/**
* @var string
*/
private $cssContent;
/**
* @var array<string, array<string, string>>
*/
private $styles = [];
public function __construct(string $cssContent)
{
$this->cssContent = $cssContent;
}
public function process(): void
{
$cssContent = str_replace(["\r", "\n"], '', $this->cssContent);
preg_match_all('/(.+?)\s?\{\s?(.+?)\s?\}/', $cssContent, $cssExtracted);
// Check the number of extracted
if (count($cssExtracted) != 3) {
return;
}
// Check if there are x selectors and x rules
if (count($cssExtracted[1]) != count($cssExtracted[2])) {
return;
}
foreach ($cssExtracted[1] as $key => $selector) {
$rules = trim($cssExtracted[2][$key]);
$rules = explode(';', $rules);
foreach ($rules as $rule) {
if (empty($rule)) {
continue;
}
[$key, $value] = explode(':', trim($rule));
$this->styles[$this->sanitize($selector)][$this->sanitize($key)] = $this->sanitize($value);
}
}
}
public function getStyles(): array
{
return $this->styles;
}
public function getStyle(string $selector): array
{
$selector = $this->sanitize($selector);
return $this->styles[$selector] ?? [];
}
private function sanitize(string $value): string
{
return addslashes(trim($value));
}
}

View File

@ -43,11 +43,6 @@ class Html
protected static $options; protected static $options;
/**
* @var Css
*/
protected static $css;
/** /**
* Add HTML parts. * Add HTML parts.
* *
@ -66,7 +61,7 @@ class Html
* @todo parse $stylesheet for default styles. Should result in an array based on id, class and element, * @todo parse $stylesheet for default styles. Should result in an array based on id, class and element,
* which could be applied when such an element occurs in the parseNode function. * which could be applied when such an element occurs in the parseNode function.
*/ */
static::$options = $options; self::$options = $options;
// Preprocess: remove all line ends, decode HTML entity, // Preprocess: remove all line ends, decode HTML entity,
// fix ampersand and angle brackets and add body tag for HTML fragments // fix ampersand and angle brackets and add body tag for HTML fragments
@ -87,10 +82,10 @@ class Html
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->preserveWhiteSpace = $preserveWhiteSpace; $dom->preserveWhiteSpace = $preserveWhiteSpace;
$dom->loadXML($html); $dom->loadXML($html);
static::$xpath = new DOMXPath($dom); self::$xpath = new DOMXPath($dom);
$node = $dom->getElementsByTagName('body'); $node = $dom->getElementsByTagName('body');
static::parseNode($node->item(0), $element); self::parseNode($node->item(0), $element);
if (\PHP_VERSION_ID < 80000) { if (\PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader($orignalLibEntityLoader); libxml_disable_entity_loader($orignalLibEntityLoader);
} }
@ -154,19 +149,6 @@ class Html
} }
} }
$attributeIdentifier = $attributes->getNamedItem('id');
if ($attributeIdentifier && self::$css) {
$styles = self::parseStyleDeclarations(self::$css->getStyle('#' . $attributeIdentifier->value), $styles);
}
$attributeClass = $attributes->getNamedItem('class');
if ($attributeClass) {
if (self::$css) {
$styles = self::parseStyleDeclarations(self::$css->getStyle('.' . $attributeClass->value), $styles);
}
$styles['className'] = $attributeClass->value;
}
$attributeStyle = $attributes->getNamedItem('style'); $attributeStyle = $attributes->getNamedItem('style');
if ($attributeStyle) { if ($attributeStyle) {
$styles = self::parseStyle($attributeStyle, $styles); $styles = self::parseStyle($attributeStyle, $styles);
@ -186,13 +168,6 @@ class Html
*/ */
protected static function parseNode($node, $element, $styles = [], $data = []): void protected static function parseNode($node, $element, $styles = [], $data = []): void
{ {
if ($node->nodeName == 'style') {
self::$css = new Css($node->textContent);
self::$css->process();
return;
}
// Populate styles array // Populate styles array
$styleTypes = ['font', 'paragraph', 'list', 'table', 'row', 'cell']; $styleTypes = ['font', 'paragraph', 'list', 'table', 'row', 'cell'];
foreach ($styleTypes as $styleType) { foreach ($styleTypes as $styleType) {
@ -414,11 +389,6 @@ class Html
$newElement = $element->addTable($elementStyles); $newElement = $element->addTable($elementStyles);
// Add style name from CSS Class
if (isset($elementStyles['className'])) {
$newElement->getStyle()->setStyleName($elementStyles['className']);
}
$attributes = $node->attributes; $attributes = $node->attributes;
if ($attributes->getNamedItem('border') !== null) { if ($attributes->getNamedItem('border') !== null) {
$border = (int) $attributes->getNamedItem('border')->value; $border = (int) $attributes->getNamedItem('border')->value;
@ -444,11 +414,7 @@ class Html
$rowStyles['tblHeader'] = true; $rowStyles['tblHeader'] = true;
} }
// set cell height to control row heights return $element->addRow(null, $rowStyles);
$height = $rowStyles['height'] ?? null;
unset($rowStyles['height']); // would not apply
return $element->addRow($height, $rowStyles);
} }
/** /**
@ -669,21 +635,13 @@ class Html
{ {
$properties = explode(';', trim($attribute->value, " \t\n\r\0\x0B;")); $properties = explode(';', trim($attribute->value, " \t\n\r\0\x0B;"));
$selectors = [];
foreach ($properties as $property) { foreach ($properties as $property) {
[$cKey, $cValue] = array_pad(explode(':', $property, 2), 2, null); [$cKey, $cValue] = array_pad(explode(':', $property, 2), 2, null);
$selectors[strtolower(trim($cKey))] = trim($cValue ?? ''); $cValue = trim($cValue ?? '');
} $cKey = strtolower(trim($cKey));
switch ($cKey) {
return self::parseStyleDeclarations($selectors, $styles);
}
protected static function parseStyleDeclarations(array $selectors, array $styles)
{
foreach ($selectors as $property => $value) {
switch ($property) {
case 'text-decoration': case 'text-decoration':
switch ($value) { switch ($cValue) {
case 'underline': case 'underline':
$styles['underline'] = 'single'; $styles['underline'] = 'single';
@ -696,44 +654,44 @@ class Html
break; break;
case 'text-align': case 'text-align':
$styles['alignment'] = self::mapAlign($value); $styles['alignment'] = self::mapAlign($cValue);
break; break;
case 'display': case 'display':
$styles['hidden'] = $value === 'none' || $value === 'hidden'; $styles['hidden'] = $cValue === 'none' || $cValue === 'hidden';
break; break;
case 'direction': case 'direction':
$styles['rtl'] = $value === 'rtl'; $styles['rtl'] = $cValue === 'rtl';
break; break;
case 'font-size': case 'font-size':
$styles['size'] = Converter::cssToPoint($value); $styles['size'] = Converter::cssToPoint($cValue);
break; break;
case 'font-family': case 'font-family':
$value = array_map('trim', explode(',', $value)); $cValue = array_map('trim', explode(',', $cValue));
$styles['name'] = ucwords($value[0]); $styles['name'] = ucwords($cValue[0]);
break; break;
case 'color': case 'color':
$styles['color'] = trim($value, '#'); $styles['color'] = trim($cValue, '#');
break; break;
case 'background-color': case 'background-color':
$styles['bgColor'] = trim($value, '#'); $styles['bgColor'] = trim($cValue, '#');
break; break;
case 'line-height': case 'line-height':
$matches = []; $matches = [];
if ($value === 'normal') { if ($cValue === 'normal') {
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
$spacing = 0; $spacing = 0;
} elseif (preg_match('/([0-9]+\.?[0-9]*[a-z]+)/', $value, $matches)) { } elseif (preg_match('/([0-9]+\.?[0-9]*[a-z]+)/', $cValue, $matches)) {
//matches number with a unit, e.g. 12px, 15pt, 20mm, ... //matches number with a unit, e.g. 12px, 15pt, 20mm, ...
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT; $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT;
$spacing = Converter::cssToTwip($matches[1]); $spacing = Converter::cssToTwip($matches[1]);
} elseif (preg_match('/([0-9]+)%/', $value, $matches)) { } elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
//matches percentages //matches percentages
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
//we are subtracting 1 line height because the Spacing writer is adding one line //we are subtracting 1 line height because the Spacing writer is adding one line
@ -742,23 +700,23 @@ class Html
//any other, wich is a multiplier. E.g. 1.2 //any other, wich is a multiplier. E.g. 1.2
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
//we are subtracting 1 line height because the Spacing writer is adding one line //we are subtracting 1 line height because the Spacing writer is adding one line
$spacing = ($value * Paragraph::LINE_HEIGHT) - Paragraph::LINE_HEIGHT; $spacing = ($cValue * Paragraph::LINE_HEIGHT) - Paragraph::LINE_HEIGHT;
} }
$styles['spacingLineRule'] = $spacingLineRule; $styles['spacingLineRule'] = $spacingLineRule;
$styles['line-spacing'] = $spacing; $styles['line-spacing'] = $spacing;
break; break;
case 'letter-spacing': case 'letter-spacing':
$styles['letter-spacing'] = Converter::cssToTwip($value); $styles['letter-spacing'] = Converter::cssToTwip($cValue);
break; break;
case 'text-indent': case 'text-indent':
$styles['indentation']['firstLine'] = Converter::cssToTwip($value); $styles['indentation']['firstLine'] = Converter::cssToTwip($cValue);
break; break;
case 'font-weight': case 'font-weight':
$tValue = false; $tValue = false;
if (preg_match('#bold#', $value)) { if (preg_match('#bold#', $cValue)) {
$tValue = true; // also match bolder $tValue = true; // also match bolder
} }
$styles['bold'] = $tValue; $styles['bold'] = $tValue;
@ -766,57 +724,52 @@ class Html
break; break;
case 'font-style': case 'font-style':
$tValue = false; $tValue = false;
if (preg_match('#(?:italic|oblique)#', $value)) { if (preg_match('#(?:italic|oblique)#', $cValue)) {
$tValue = true; $tValue = true;
} }
$styles['italic'] = $tValue; $styles['italic'] = $tValue;
break; break;
case 'margin': case 'margin':
$value = Converter::cssToTwip($value); $cValue = Converter::cssToTwip($cValue);
$styles['spaceBefore'] = $value; $styles['spaceBefore'] = $cValue;
$styles['spaceAfter'] = $value; $styles['spaceAfter'] = $cValue;
break; break;
case 'margin-top': case 'margin-top':
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value) // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue)
$styles['spaceBefore'] = Converter::cssToTwip($value); $styles['spaceBefore'] = Converter::cssToTwip($cValue);
break; break;
case 'margin-bottom': case 'margin-bottom':
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value) // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue)
$styles['spaceAfter'] = Converter::cssToTwip($value); $styles['spaceAfter'] = Converter::cssToTwip($cValue);
break; break;
case 'border-color': case 'border-color':
self::mapBorderColor($styles, $value); self::mapBorderColor($styles, $cValue);
break; break;
case 'border-width': case 'border-width':
$styles['borderSize'] = Converter::cssToPoint($value); $styles['borderSize'] = Converter::cssToPoint($cValue);
break; break;
case 'border-style': case 'border-style':
$styles['borderStyle'] = self::mapBorderStyle($value); $styles['borderStyle'] = self::mapBorderStyle($cValue);
break; break;
case 'width': case 'width':
if (preg_match('/([0-9]+[a-z]+)/', $value, $matches)) { if (preg_match('/([0-9]+[a-z]+)/', $cValue, $matches)) {
$styles['width'] = Converter::cssToTwip($matches[1]); $styles['width'] = Converter::cssToTwip($matches[1]);
$styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP; $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP;
} elseif (preg_match('/([0-9]+)%/', $value, $matches)) { } elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
$styles['width'] = $matches[1] * 50; $styles['width'] = $matches[1] * 50;
$styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT; $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT;
} elseif (preg_match('/([0-9]+)/', $value, $matches)) { } elseif (preg_match('/([0-9]+)/', $cValue, $matches)) {
$styles['width'] = $matches[1]; $styles['width'] = $matches[1];
$styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::AUTO; $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::AUTO;
} }
break;
case 'height':
$styles['height'] = Converter::cssToTwip($value);
$styles['exactHeight'] = true;
break; break;
case 'border': case 'border':
case 'border-top': case 'border-top':
@ -825,9 +778,9 @@ class Html
case 'border-left': case 'border-left':
// must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid" // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid"
// Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $value, $matches)) { if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $cValue, $matches)) {
if (false !== strpos($property, '-')) { if (false !== strpos($cKey, '-')) {
$tmp = explode('-', $property); $tmp = explode('-', $cKey);
$which = $tmp[1]; $which = $tmp[1];
$which = ucfirst($which); // e.g. bottom -> Bottom $which = ucfirst($which); // e.g. bottom -> Bottom
} else { } else {
@ -850,13 +803,13 @@ class Html
break; break;
case 'vertical-align': case 'vertical-align':
// https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align // https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align
if (preg_match('#(?:top|bottom|middle|sub|baseline)#i', $value, $matches)) { if (preg_match('#(?:top|bottom|middle|sub|baseline)#i', $cValue, $matches)) {
$styles['valign'] = self::mapAlignVertical($matches[0]); $styles['valign'] = self::mapAlignVertical($matches[0]);
} }
break; break;
case 'page-break-after': case 'page-break-after':
if ($value == 'always') { if ($cValue == 'always') {
$styles['isPageBreak'] = true; $styles['isPageBreak'] = true;
} }

View File

@ -286,7 +286,7 @@ class Cell extends Border
*/ */
public function setWidth($value) public function setWidth($value)
{ {
$this->width = $this->setIntVal($value); $this->setIntVal($value);
return $this; return $this;
} }

View File

@ -70,9 +70,6 @@ final class Language extends AbstractStyle
const NL_NL = 'nl-NL'; const NL_NL = 'nl-NL';
const NL_NL_ID = 1043; const NL_NL_ID = 1043;
const SV_SE = 'sv-SE';
const SV_SE_ID = 1053;
const UK_UA = 'uk-UA'; const UK_UA = 'uk-UA';
const UK_UA_ID = 1058; const UK_UA_ID = 1058;

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -25,128 +27,121 @@ class TextBox extends Image
/** /**
* margin top. * margin top.
* *
* @var null|int * @var int
*/ */
private $innerMarginTop; private $innerMarginTop;
/** /**
* margin left. * margin left.
* *
* @var null|int * @var int
*/ */
private $innerMarginLeft; private $innerMarginLeft;
/** /**
* margin right. * margin right.
* *
* @var null|int * @var int
*/ */
private $innerMarginRight; private $innerMarginRight;
/** /**
* Cell margin bottom. * Cell margin bottom.
* *
* @var null|int * @var int
*/ */
private $innerMarginBottom; private $innerMarginBottom;
/** /**
* border size. * border size.
* *
* @var null|int * @var int
*/ */
private $borderSize; private $borderSize;
/** /**
* border color. * border color.
* *
* @var null|string * @var string
*/ */
private $borderColor; private $borderColor;
/**
* background color.
*
* @var null|string
*/
private $bgColor;
/**
* Set background color.
*/
public function setBgColor(?string $value = null): void
{
$this->bgColor = $value;
}
/**
* Get background color.
*/
public function getBgColor(): ?string
{
return $this->bgColor;
}
/** /**
* Set margin top. * Set margin top.
*
* @param int $value
*/ */
public function setInnerMarginTop(?int $value = null): void public function setInnerMarginTop($value = null): void
{ {
$this->innerMarginTop = $value; $this->innerMarginTop = $value;
} }
/** /**
* Get margin top. * Get margin top.
*
* @return int
*/ */
public function getInnerMarginTop(): ?int public function getInnerMarginTop()
{ {
return $this->innerMarginTop; return $this->innerMarginTop;
} }
/** /**
* Set margin left. * Set margin left.
*
* @param int $value
*/ */
public function setInnerMarginLeft(?int $value = null): void public function setInnerMarginLeft($value = null): void
{ {
$this->innerMarginLeft = $value; $this->innerMarginLeft = $value;
} }
/** /**
* Get margin left. * Get margin left.
*
* @return int
*/ */
public function getInnerMarginLeft(): ?int public function getInnerMarginLeft()
{ {
return $this->innerMarginLeft; return $this->innerMarginLeft;
} }
/** /**
* Set margin right. * Set margin right.
*
* @param int $value
*/ */
public function setInnerMarginRight(?int $value = null): void public function setInnerMarginRight($value = null): void
{ {
$this->innerMarginRight = $value; $this->innerMarginRight = $value;
} }
/** /**
* Get margin right. * Get margin right.
*
* @return int
*/ */
public function getInnerMarginRight(): ?int public function getInnerMarginRight()
{ {
return $this->innerMarginRight; return $this->innerMarginRight;
} }
/** /**
* Set margin bottom. * Set margin bottom.
*
* @param int $value
*/ */
public function setInnerMarginBottom(?int $value = null): void public function setInnerMarginBottom($value = null): void
{ {
$this->innerMarginBottom = $value; $this->innerMarginBottom = $value;
} }
/** /**
* Get margin bottom. * Get margin bottom.
*
* @return int
*/ */
public function getInnerMarginBottom(): ?int public function getInnerMarginBottom()
{ {
return $this->innerMarginBottom; return $this->innerMarginBottom;
} }
@ -154,9 +149,9 @@ class TextBox extends Image
/** /**
* Set TLRB cell margin. * Set TLRB cell margin.
* *
* @param null|int $value Margin in twips * @param int $value Margin in twips
*/ */
public function setInnerMargin(?int $value = null): void public function setInnerMargin($value = null): void
{ {
$this->setInnerMarginTop($value); $this->setInnerMarginTop($value);
$this->setInnerMarginLeft($value); $this->setInnerMarginLeft($value);
@ -169,15 +164,17 @@ class TextBox extends Image
* *
* @return int[] * @return int[]
*/ */
public function getInnerMargin(): array public function getInnerMargin()
{ {
return [$this->innerMarginLeft, $this->innerMarginTop, $this->innerMarginRight, $this->innerMarginBottom]; return [$this->innerMarginLeft, $this->innerMarginTop, $this->innerMarginRight, $this->innerMarginBottom];
} }
/** /**
* Has inner margin? * Has inner margin?
*
* @return bool
*/ */
public function hasInnerMargins(): bool public function hasInnerMargins()
{ {
$hasInnerMargins = false; $hasInnerMargins = false;
$margins = $this->getInnerMargin(); $margins = $this->getInnerMargin();
@ -194,33 +191,39 @@ class TextBox extends Image
/** /**
* Set border size. * Set border size.
* *
* @param null|int $value Size in points * @param int $value Size in points
*/ */
public function setBorderSize(?int $value = null): void public function setBorderSize($value = null): void
{ {
$this->borderSize = $value; $this->borderSize = $value;
} }
/** /**
* Get border size. * Get border size.
*
* @return int
*/ */
public function getBorderSize(): ?int public function getBorderSize()
{ {
return $this->borderSize; return $this->borderSize;
} }
/** /**
* Set border color. * Set border color.
*
* @param string $value
*/ */
public function setBorderColor(?string $value = null): void public function setBorderColor($value = null): void
{ {
$this->borderColor = $value; $this->borderColor = $value;
} }
/** /**
* Get border color. * Get border color.
*
* @return string
*/ */
public function getBorderColor(): ?string public function getBorderColor()
{ {
return $this->borderColor; return $this->borderColor;
} }

View File

@ -94,10 +94,6 @@ class TemplateProcessor
*/ */
protected $tempDocumentNewImages = []; protected $tempDocumentNewImages = [];
protected static $macroOpeningChars = '${';
protected static $macroClosingChars = '}';
/** /**
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception
* *
@ -242,8 +238,8 @@ class TemplateProcessor
*/ */
protected static function ensureMacroCompleted($macro) protected static function ensureMacroCompleted($macro)
{ {
if (substr($macro, 0, 2) !== self::$macroOpeningChars && substr($macro, -1) !== self::$macroClosingChars) { if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
$macro = self::$macroOpeningChars . $macro . self::$macroClosingChars; $macro = '${' . $macro . '}';
} }
return $macro; return $macro;
@ -763,70 +759,6 @@ class TemplateProcessor
$this->tempDocumentMainPart = $result; $this->tempDocumentMainPart = $result;
} }
/**
* Delete a table row in a template document.
*/
public function deleteRow(string $search): void
{
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
$search = '${' . $search . '}';
}
$tagPos = strpos($this->tempDocumentMainPart, $search);
if (!$tagPos) {
throw new Exception(sprintf('Can not delete row %s, template variable not found or variable contains markup.', $search));
}
$tableStart = $this->findTableStart($tagPos);
$tableEnd = $this->findTableEnd($tagPos);
$xmlTable = $this->getSlice($tableStart, $tableEnd);
if (substr_count($xmlTable, '<w:tr') === 1) {
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
return;
}
$rowStart = $this->findRowStart($tagPos);
$rowEnd = $this->findRowEnd($tagPos);
$xmlRow = $this->getSlice($rowStart, $rowEnd);
$this->tempDocumentMainPart = $this->getSlice(0, $rowStart) . $this->getSlice($rowEnd);
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
$extraRowStart = $rowStart;
while (true) {
$extraRowStart = $this->findRowStart($extraRowStart + 1);
$extraRowEnd = $this->findRowEnd($extraRowStart + 1);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)
) {
break;
}
$tableStart = $this->findTableStart($extraRowEnd + 1);
$tableEnd = $this->findTableEnd($extraRowEnd + 1);
$xmlTable = $this->getSlice($tableStart, $tableEnd);
if (substr_count($xmlTable, '<w:tr') === 1) {
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
return;
}
$this->tempDocumentMainPart = $this->getSlice(0, $extraRowStart) . $this->getSlice($extraRowEnd);
}
}
}
/** /**
* Clones a table row and populates it's values from a two-dimensional array in a template document. * Clones a table row and populates it's values from a two-dimensional array in a template document.
* *
@ -860,12 +792,8 @@ class TemplateProcessor
{ {
$xmlBlock = null; $xmlBlock = null;
$matches = []; $matches = [];
$escapedMacroOpeningChars = self::$macroOpeningChars;
$escapedMacroClosingChars = self::$macroClosingChars;
preg_match( preg_match(
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\{{' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\{{\/' . $blockname . '}<\/w:.*?p>)/is', '/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\${' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\${\/' . $blockname . '}<\/w:.*?p>)/is',
'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\\' . $escapedMacroOpeningChars . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\\' . $escapedMacroOpeningChars . '\/' . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)/is',
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\\'. $escapedMacroOpeningChars . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\\'.$escapedMacroOpeningChars.'\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart, $this->tempDocumentMainPart,
$matches $matches
); );
@ -904,10 +832,8 @@ class TemplateProcessor
public function replaceBlock($blockname, $replacement): void public function replaceBlock($blockname, $replacement): void
{ {
$matches = []; $matches = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
preg_match( preg_match(
'/(<\?xml.*)(<w:p.*>' . $escapedMacroOpeningChars . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)(.*)(<w:p.*' . $escapedMacroOpeningChars . '\/' . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)/is', '/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart, $this->tempDocumentMainPart,
$matches $matches
); );
@ -1023,12 +949,8 @@ class TemplateProcessor
*/ */
protected function fixBrokenMacros($documentPart) protected function fixBrokenMacros($documentPart)
{ {
$brokenMacroOpeningChars = substr(self::$macroOpeningChars, 0, 1);
$endMacroOpeningChars = substr(self::$macroOpeningChars, 1);
$macroClosingChars = self::$macroClosingChars;
return preg_replace_callback( return preg_replace_callback(
'/\\' . $brokenMacroOpeningChars . '(?:\\' . $endMacroOpeningChars . '|[^{$]*\>\{)[^' . $macroClosingChars . '$]*\}/U', '/\$(?:\{|[^{$]*\>\{)[^}$]*\}/U',
function ($match) { function ($match) {
return strip_tags($match[0]); return strip_tags($match[0]);
}, },
@ -1041,7 +963,7 @@ class TemplateProcessor
* *
* @param mixed $search * @param mixed $search
* @param mixed $replace * @param mixed $replace
* @param array<int, string>|string $documentPartXML * @param string $documentPartXML
* @param int $limit * @param int $limit
* *
* @return string * @return string
@ -1067,10 +989,7 @@ class TemplateProcessor
protected function getVariablesForPart($documentPartXML) protected function getVariablesForPart($documentPartXML)
{ {
$matches = []; $matches = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars); preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
preg_match_all("/$escapedMacroOpeningChars(.*?)$escapedMacroClosingChars/i", $documentPartXML, $matches);
return $matches[1]; return $matches[1];
} }
@ -1160,39 +1079,6 @@ class TemplateProcessor
return '[Content_Types].xml'; return '[Content_Types].xml';
} }
/**
* Find the start position of the nearest table before $offset.
*/
private function findTableStart(int $offset): int
{
$rowStart = strrpos(
$this->tempDocumentMainPart,
'<w:tbl ',
((strlen($this->tempDocumentMainPart) - $offset) * -1)
);
if (!$rowStart) {
$rowStart = strrpos(
$this->tempDocumentMainPart,
'<w:tbl>',
((strlen($this->tempDocumentMainPart) - $offset) * -1)
);
}
if (!$rowStart) {
throw new Exception('Can not find the start position of the table.');
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*/
private function findTableEnd(int $offset): int
{
return strpos($this->tempDocumentMainPart, '</w:tbl>', $offset) + 7;
}
/** /**
* Find the start position of the nearest table row before $offset. * Find the start position of the nearest table row before $offset.
* *
@ -1255,11 +1141,8 @@ class TemplateProcessor
protected function indexClonedVariables($count, $xmlBlock) protected function indexClonedVariables($count, $xmlBlock)
{ {
$results = []; $results = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
for ($i = 1; $i <= $count; ++$i) { for ($i = 1; $i <= $count; ++$i) {
$results[] = preg_replace("/$escapedMacroOpeningChars([^:]*?)(:.*?)?$escapedMacroClosingChars/", self::$macroOpeningChars . '\1#' . $i . '\2' . self::$macroClosingChars, $xmlBlock); $results[] = preg_replace('/\$\{([^:]*?)(:.*?)?\}/', '\${\1#' . $i . '\2}', $xmlBlock);
} }
return $results; return $results;
@ -1414,7 +1297,7 @@ class TemplateProcessor
} }
$unformattedText = preg_replace('/>\s+</', '><', $text); $unformattedText = preg_replace('/>\s+</', '><', $text);
$result = str_replace([self::$macroOpeningChars, self::$macroClosingChars], ['</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">' . self::$macroOpeningChars, self::$macroClosingChars . '</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">'], $unformattedText); $result = str_replace(['${', '}'], ['</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(['<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>'], ['', '', '<w:t xml:space="preserve">'], $result); return str_replace(['<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>'], ['', '', '<w:t xml:space="preserve">'], $result);
} }
@ -1428,25 +1311,6 @@ class TemplateProcessor
*/ */
protected function textNeedsSplitting($text) protected function textNeedsSplitting($text)
{ {
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars); return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
return 1 === preg_match('/[^>]' . $escapedMacroOpeningChars . '|' . $escapedMacroClosingChars . '[^<]/i', $text);
}
public function setMacroOpeningChars(string $macroOpeningChars): void
{
self::$macroOpeningChars = $macroOpeningChars;
}
public function setMacroClosingChars(string $macroClosingChars): void
{
self::$macroClosingChars = $macroClosingChars;
}
public function setMacroChars(string $macroOpeningChars, string $macroClosingChars): void
{
self::$macroOpeningChars = $macroOpeningChars;
self::$macroClosingChars = $macroClosingChars;
} }
} }

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -48,10 +50,6 @@ class TextBox extends Image
$xmlWriter->startElement('v:shape'); $xmlWriter->startElement('v:shape');
$xmlWriter->writeAttribute('type', '#_x0000_t0202'); $xmlWriter->writeAttribute('type', '#_x0000_t0202');
if ($style->getBgColor()) {
$xmlWriter->writeAttribute('fillcolor', $style->getBgColor());
}
$styleWriter->write(); $styleWriter->write();
$styleWriter->writeBorder(); $styleWriter->writeBorder();

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -17,15 +19,15 @@ namespace PhpOffice\PhpWordTests;
use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Settings;
use PHPUnit\Framework\TestCase;
/** /**
* Test class for PhpOffice\PhpWord\Settings. * Test class for PhpOffice\PhpWord\Settings.
* *
* @coversDefaultClass \PhpOffice\PhpWord\Settings * @coversDefaultClass \PhpOffice\PhpWord\Settings
*
* @runTestsInSeparateProcesses * @runTestsInSeparateProcesses
*/ */
class SettingsTest extends TestCase class SettingsTest extends \PHPUnit\Framework\TestCase
{ {
private $compatibility; private $compatibility;
@ -149,6 +151,7 @@ class SettingsTest extends TestCase
/** /**
* @covers ::getTempDir * @covers ::getTempDir
* @covers ::setTempDir * @covers ::setTempDir
*
* @depends testPhpTempDirIsUsedByDefault * @depends testPhpTempDirIsUsedByDefault
*/ */
public function testTempDirCanBeSet(): void public function testTempDirCanBeSet(): void
@ -186,12 +189,6 @@ class SettingsTest extends TestCase
self::assertEquals(12, Settings::getDefaultFontSize()); self::assertEquals(12, Settings::getDefaultFontSize());
self::assertFalse(Settings::setDefaultFontSize(null)); self::assertFalse(Settings::setDefaultFontSize(null));
self::assertEquals(12, Settings::getDefaultFontSize()); self::assertEquals(12, Settings::getDefaultFontSize());
self::assertTrue(Settings::setDefaultFontSize(12.5));
self::assertEquals(12.5, Settings::getDefaultFontSize());
self::assertFalse(Settings::setDefaultFontSize(0.5));
self::assertEquals(12.5, Settings::getDefaultFontSize());
self::assertFalse(Settings::setDefaultFontSize(0));
self::assertEquals(12.5, Settings::getDefaultFontSize());
} }
/** /**

View File

@ -1,54 +0,0 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @see https://github.com/PHPOffice/PHPWord
*
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWordTests\Shared;
use PhpOffice\PhpWord\Shared\Css;
use PHPUnit\Framework\TestCase;
/**
* Test class for PhpOffice\PhpWord\Shared\Css.
*/
class CssTest extends TestCase
{
public function testEmptyCss(): void
{
$css = new Css('');
$css->process();
self::assertEquals([], $css->getStyles());
}
public function testBasicCss(): void
{
$cssContent = '.pStyle {
font-size:15px;
}';
$css = new Css($cssContent);
$css->process();
self::assertEquals([
'.pStyle' => [
'font-size' => '15px',
],
], $css->getStyles());
self::assertEquals([
'font-size' => '15px',
], $css->getStyle('.pStyle'));
}
}

View File

@ -19,7 +19,6 @@ namespace PhpOffice\PhpWordTests\Shared;
use Exception; use Exception;
use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Shared\Html; use PhpOffice\PhpWord\Shared\Html;
use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\Jc;
@ -108,44 +107,6 @@ class HtmlTest extends AbstractWebServerEmbeddedTest
self::assertEquals('text with entities <my text>', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue); self::assertEquals('text with entities <my text>', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->nodeValue);
} }
public function testParseStyle(): void
{
$html = '<style type="text/css">
.pStyle {
font-size:15px;
}
.tableStyle {
width:100%;
background-color:red;
}
</style>
<p class="pStyle">Calculator</p>';
$phpWord = new PhpWord();
$section = $phpWord->addSection();
Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]'));
self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r'));
self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:t'));
self::assertEquals('Calculator', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue);
self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:rPr'));
self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz'));
self::assertEquals('22.5', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz', 'w:val'));
}
public function testParseStyleTableClassName(): void
{
$html = '<style type="text/css">.pStyle { font-size:15px; }</style><table class="pStyle"><tr><td></td></tr></table>';
$phpWord = new PhpWord();
$section = $phpWord->addSection();
Html::addHtml($section, $html);
self::assertInstanceOf(Table::class, $section->getElement(0));
self::assertEquals('pStyle', $section->getElement(0)->getStyle()->getStyleName());
}
/** /**
* Test underline. * Test underline.
*/ */
@ -464,58 +425,6 @@ HTML;
self::assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); self::assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type'));
} }
/**
* Parse heights in rows, which also allows for controlling column height.
*/
public function testParseTableRowHeight(): void
{
$phpWord = new PhpWord();
$section = $phpWord->addSection([
'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE,
]);
$html = <<<HTML
<table>
<tr style="height: 100px;">
<td>100px</td>
</tr>
<tr style="height: 200pt;">
<td>200pt</td>
</tr>
<tr>
<td>
<table>
<tr style="height: 300px;">
<td>300px</td>
</tr>
</table>
</td>
</tr>
</table>
HTML;
Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
// <tr style="height: 100; ... 100px = 1500 twips (100 / 96 * 1440)
$xpath = '/w:document/w:body/w:tbl/w:tr/w:trPr/w:trHeight';
self::assertTrue($doc->elementExists($xpath));
self::assertEquals(1500, $doc->getElement($xpath)->getAttribute('w:val'));
self::assertEquals('exact', $doc->getElement($xpath)->getAttribute('w:hRule'));
// <tr style="height: 200pt; ... 200pt = 4000 twips (200 / 72 * 1440)
$xpath = '/w:document/w:body/w:tbl/w:tr[2]/w:trPr/w:trHeight';
self::assertTrue($doc->elementExists($xpath));
self::assertEquals(4000, $doc->getElement($xpath)->getAttribute('w:val'));
self::assertEquals('exact', $doc->getElement($xpath)->getAttribute('w:hRule'));
// <tr style="width: 300; .. 300px = 4500 twips (300 / 72 * 1440)
$xpath = '/w:document/w:body/w:tbl/w:tr[3]/w:tc/w:tbl/w:tr/w:trPr/w:trHeight';
self::assertTrue($doc->elementExists($xpath));
self::assertEquals(4500, $doc->getElement($xpath)->getAttribute('w:val'));
self::assertEquals('exact', $doc->getElement($xpath)->getAttribute('w:hRule'));
}
/** /**
* Test parsing table (attribute border). * Test parsing table (attribute border).
*/ */

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -18,15 +20,15 @@ namespace PhpOffice\PhpWordTests\Style;
use InvalidArgumentException; use InvalidArgumentException;
use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\TextBox; use PhpOffice\PhpWord\Style\TextBox;
use PHPUnit\Framework\TestCase;
/** /**
* Test class for PhpOffice\PhpWord\Style\Image. * Test class for PhpOffice\PhpWord\Style\Image.
* *
* @coversDefaultClass \PhpOffice\PhpWord\Style\Image * @coversDefaultClass \PhpOffice\PhpWord\Style\Image
*
* @runTestsInSeparateProcesses * @runTestsInSeparateProcesses
*/ */
class TextBoxTest extends TestCase class TextBoxTest extends \PHPUnit\Framework\TestCase
{ {
/** /**
* Test setting style with normal value. * Test setting style with normal value.
@ -53,7 +55,6 @@ class TextBoxTest extends TestCase
'innerMarginLeft' => '5', 'innerMarginLeft' => '5',
'borderSize' => '2', 'borderSize' => '2',
'borderColor' => 'red', 'borderColor' => 'red',
'bgColor' => 'blue',
]; ];
foreach ($properties as $key => $value) { foreach ($properties as $key => $value) {
$set = "set{$key}"; $set = "set{$key}";
@ -88,7 +89,6 @@ class TextBoxTest extends TestCase
'innerMarginLeft' => '5', 'innerMarginLeft' => '5',
'borderSize' => '2', 'borderSize' => '2',
'borderColor' => 'red', 'borderColor' => 'red',
'bgColor' => 'blue',
]; ];
foreach ($properties as $key => $value) { foreach ($properties as $key => $value) {
$get = "get{$key}"; $get = "get{$key}";
@ -305,15 +305,4 @@ class TextBoxTest extends TestCase
$object->setBorderColor($expected); $object->setBorderColor($expected);
self::assertEquals($expected, $object->getBorderColor()); self::assertEquals($expected, $object->getBorderColor());
} }
/**
* Test set/get bgColor.
*/
public function testSetGetBgColor(): void
{
$expected = 'blue';
$object = new TextBox();
$object->setBgColor($expected);
self::assertEquals($expected, $object->getBgColor());
}
} }

View File

@ -182,32 +182,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
@$templateProcessor->applyXslStyleSheet($xslDomDocument); @$templateProcessor->applyXslStyleSheet($xslDomDocument);
} }
/**
* @covers ::deleteRow
* @covers ::getVariables
* @covers ::saveAs
*/
public function testDeleteRow(): void
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
self::assertEquals(
['deleteMe', 'deleteMeToo'],
$templateProcessor->getVariables()
);
$docName = 'delete-row-test-result.docx';
$templateProcessor->deleteRow('deleteMe');
self::assertEquals(
[],
$templateProcessor->getVariables()
);
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);
self::assertTrue($docFound);
}
/** /**
* @covers ::cloneRow * @covers ::cloneRow
* @covers ::saveAs * @covers ::saveAs
@ -232,33 +206,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertTrue($docFound); self::assertTrue($docFound);
} }
/**
* @covers ::cloneRow
* @covers ::saveAs
* @covers ::setValue
*/
public function testCloneRowWithCustomMacro(): void
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
$templateProcessor->setMacroOpeningChars('{#');
$templateProcessor->setMacroClosingChars('#}');
self::assertEquals(
['tableHeader', 'userId', 'userName', 'userLocation'],
$templateProcessor->getVariables()
);
$docName = 'clone-test-result.docx';
$templateProcessor->setValue('tableHeader', utf8_decode('ééé'));
$templateProcessor->cloneRow('userId', 1);
$templateProcessor->setValue('userId#1', 'Test');
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);
self::assertTrue($docFound);
}
/** /**
* @covers ::cloneRow * @covers ::cloneRow
* @covers ::saveAs * @covers ::saveAs
@ -328,68 +275,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
$templateProcessor->cloneRow('fake_search', 2); $templateProcessor->cloneRow('fake_search', 2);
} }
/**
* @covers ::cloneRow
* @covers ::saveAs
* @covers ::setValue
*/
public function testCloneRowAndSetValuesWithCustomMacro(): void
{
$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);
$templateProcessor->setMacroOpeningChars('{{');
$templateProcessor->setMacroClosingChars('}}');
self::assertEquals(
['userId', 'userName', 'userLocation'],
$templateProcessor->getVariables()
);
$values = [
['userId' => 1, 'userName' => 'Batman', 'userLocation' => 'Gotham City'],
['userId' => 2, 'userName' => 'Superman', 'userLocation' => 'Metropolis'],
];
$templateProcessor->setValue('tableHeader', 'My clonable table');
$templateProcessor->cloneRowAndSetValues('userId', $values);
self::assertStringContainsString('<w:t>Superman</w:t>', $templateProcessor->getMainPart());
self::assertStringContainsString('<w:t>Metropolis</w:t>', $templateProcessor->getMainPart());
}
/** /**
* @covers ::saveAs * @covers ::saveAs
* @covers ::setValue * @covers ::setValue
@ -411,29 +296,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertTrue($docFound); self::assertTrue($docFound);
} }
/**
* @covers ::saveAs
* @covers ::setValue
*/
public function testCustomMacrosCanBeReplacedInHeaderAndFooter(): void
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer-with-custom-macro.docx');
$templateProcessor->setMacroOpeningChars('{{');
$templateProcessor->setMacroClosingChars('}}');
self::assertEquals(['documentContent', 'headerValue:100:100', 'footerValue'], $templateProcessor->getVariables());
$macroNames = ['headerValue', 'documentContent', 'footerValue'];
$macroValues = ['Header Value', 'Document text.', 'Footer Value'];
$templateProcessor->setValue($macroNames, $macroValues);
$docName = 'header-footer-test-result.docx';
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);
self::assertTrue($docFound);
}
/** /**
* @covers ::setValue * @covers ::setValue
*/ */
@ -449,22 +311,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
); );
} }
/**
* @covers ::setValue
*/
public function testSetValueWithCustomMacro(): void
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
$templateProcessor->setMacroChars('{#', '#}');
Settings::setOutputEscapingEnabled(true);
$helloworld = "hello\nworld";
$templateProcessor->setValue('userName', $helloworld);
self::assertEquals(
['tableHeader', 'userId', 'userLocation'],
$templateProcessor->getVariables()
);
}
public function testSetComplexValue(): void public function testSetComplexValue(): void
{ {
$title = new TextRun(); $title = new TextRun();
@ -518,60 +364,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals(preg_replace('/>\s+</', '><', $result), preg_replace('/>\s+</', '><', $templateProcessor->getMainPart())); self::assertEquals(preg_replace('/>\s+</', '><', $result), preg_replace('/>\s+</', '><', $templateProcessor->getMainPart()));
} }
public function testSetComplexValueWithCustomMacro(): void
{
$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->setMacroChars('{{', '}}');
$templateProcessor->setComplexBlock('document-title', $title);
$templateProcessor->setComplexValue('firstname', $firstname);
$templateProcessor->setComplexValue('lastname', $lastname);
self::assertEquals(preg_replace('/>\s+</', '><', $result), preg_replace('/>\s+</', '><', $templateProcessor->getMainPart()));
}
/** /**
* @covers ::setValues * @covers ::setValues
*/ */
@ -590,25 +382,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertStringContainsString('Hello John Doe', $templateProcessor->getMainPart()); self::assertStringContainsString('Hello John Doe', $templateProcessor->getMainPart());
} }
/**
* @covers ::setValues
*/
public function testSetValuesWithCustomMacro(): void
{
$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->setMacroChars('{#', '#}');
$templateProcessor->setValues(['firstname' => 'John', 'lastname' => 'Doe']);
self::assertStringContainsString('Hello John Doe', $templateProcessor->getMainPart());
}
/** /**
* @covers ::setImageValue * @covers ::setImageValue
*/ */
@ -748,44 +521,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
); );
} }
/**
* @covers ::getVariableCount
*/
public function testGetVariableCountCountsHowManyTimesEachPlaceholderIsPresentWithCustomMacro(): void
{
// create template with placeholders
$phpWord = new PhpWord();
$section = $phpWord->addSection();
$header = $section->addHeader();
$header->addText('{{a_field_that_is_present_three_times}}');
$footer = $section->addFooter();
$footer->addText('{{a_field_that_is_present_twice}}');
$section2 = $phpWord->addSection();
$section2->addText('
{{a_field_that_is_present_one_time}}
{{a_field_that_is_present_three_times}}
{{a_field_that_is_present_twice}}
{{a_field_that_is_present_three_times}}
');
$objWriter = IOFactory::createWriter($phpWord);
$templatePath = 'test.docx';
$objWriter->save($templatePath);
$templateProcessor = new TemplateProcessor($templatePath);
$templateProcessor->setMacroChars('{{', '}}');
$variableCount = $templateProcessor->getVariableCount();
unlink($templatePath);
self::assertEquals(
[
'a_field_that_is_present_three_times' => 3,
'a_field_that_is_present_twice' => 2,
'a_field_that_is_present_one_time' => 1,
],
$variableCount
);
}
/** /**
* @covers ::cloneBlock * @covers ::cloneBlock
*/ */
@ -839,61 +574,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
} }
} }
/**
* @covers ::cloneBlock
*/
public function testCloneBlockCanCloneABlockTwiceWithCustomMacro(): void
{
// create template with placeholders and block
$phpWord = new PhpWord();
$section = $phpWord->addSection();
$documentElements = [
'Title: {{title}}',
'{{subreport}}',
'{{subreport.id}}: {{subreport.text}}. ',
'{{/subreport}}',
];
foreach ($documentElements as $documentElement) {
$section->addText($documentElement);
}
$objWriter = IOFactory::createWriter($phpWord);
$templatePath = 'test.docx';
$objWriter->save($templatePath);
// replace placeholders and save the file
$templateProcessor = new TemplateProcessor($templatePath);
$templateProcessor->setMacroChars('{{', '}}');
$templateProcessor->setValue('title', 'Some title');
$templateProcessor->cloneBlock('subreport', 2);
$templateProcessor->setValue('subreport.id', '123', 1);
$templateProcessor->setValue('subreport.text', 'Some text', 1);
$templateProcessor->setValue('subreport.id', '456', 1);
$templateProcessor->setValue('subreport.text', 'Some other text', 1);
$templateProcessor->saveAs($templatePath);
// assert the block has been cloned twice
// and the placeholders have been replaced correctly
$phpWord = IOFactory::load($templatePath);
$sections = $phpWord->getSections();
/** @var \PhpOffice\PhpWord\Element\TextRun[] $actualElements */
$actualElements = $sections[0]->getElements();
unlink($templatePath);
$expectedElements = [
'Title: Some title',
'123: Some text. ',
'456: Some other text. ',
];
self::assertCount(count($expectedElements), $actualElements);
foreach ($expectedElements as $i => $expectedElement) {
self::assertEquals(
$expectedElement,
$actualElements[$i]->getElement(0)->getText()
);
}
}
/** /**
* @covers ::cloneBlock * @covers ::cloneBlock
*/ */
@ -923,36 +603,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals(3, substr_count($templateProcessor->getMainPart(), 'This block will be cloned with ${variable}')); self::assertEquals(3, substr_count($templateProcessor->getMainPart(), 'This block will be cloned with ${variable}'));
} }
/**
* @covers ::cloneBlock
*/
public function testCloneBlockWithCustomMacro(): void
{
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
<w:p>
<w:r>
<w:rPr></w:rPr>
<w:t>{{CLONEME}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t xml:space="preserve">This block will be cloned with {{variable}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r w:rsidRPr="00204FED">
<w:t>{{/CLONEME}}</w:t>
</w:r>
</w:p>';
$templateProcessor = new TestableTemplateProcesor($mainPart);
$templateProcessor->setMacroChars('{{', '}}');
$templateProcessor->cloneBlock('CLONEME', 3);
self::assertEquals(3, substr_count($templateProcessor->getMainPart(), 'This block will be cloned with {{variable}}'));
}
/** /**
* @covers ::cloneBlock * @covers ::cloneBlock
*/ */
@ -984,38 +634,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertStringContainsString('Address ${address#3}, Street ${street#3}', $templateProcessor->getMainPart()); self::assertStringContainsString('Address ${address#3}, Street ${street#3}', $templateProcessor->getMainPart());
} }
/**
* @covers ::cloneBlock
*/
public function testCloneBlockWithVariablesAndCustomMacro(): void
{
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
<w:p>
<w:r>
<w:rPr></w:rPr>
<w:t>{{CLONEME}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t xml:space="preserve">Address {{address}}, Street {{street}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r w:rsidRPr="00204FED">
<w:t>{{/CLONEME}}</w:t>
</w:r>
</w:p>';
$templateProcessor = new TestableTemplateProcesor($mainPart);
$templateProcessor->setMacroChars('{{', '}}');
$templateProcessor->cloneBlock('CLONEME', 3, true, true);
self::assertStringContainsString('Address {{address#1}}, Street {{street#1}}', $templateProcessor->getMainPart());
self::assertStringContainsString('Address {{address#2}}, Street {{street#2}}', $templateProcessor->getMainPart());
self::assertStringContainsString('Address {{address#3}}, Street {{street#3}}', $templateProcessor->getMainPart());
}
public function testCloneBlockWithVariableReplacements(): void public function testCloneBlockWithVariableReplacements(): void
{ {
$mainPart = '<?xml version="1.0" encoding="UTF-8"?> $mainPart = '<?xml version="1.0" encoding="UTF-8"?>
@ -1049,40 +667,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertStringContainsString('City: Rome, Street: Via della Conciliazione', $templateProcessor->getMainPart()); self::assertStringContainsString('City: Rome, Street: Via della Conciliazione', $templateProcessor->getMainPart());
} }
public function testCloneBlockWithVariableReplacementsAndCustomMacro(): void
{
$mainPart = '<?xml version="1.0" encoding="UTF-8"?>
<w:p>
<w:r>
<w:rPr></w:rPr>
<w:t>{{CLONEME}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t xml:space="preserve">City: {{city}}, Street: {{street}}</w:t>
</w:r>
</w:p>
<w:p>
<w:r w:rsidRPr="00204FED">
<w:t>{{/CLONEME}}</w:t>
</w:r>
</w:p>';
$replacements = [
['city' => 'London', 'street' => 'Baker Street'],
['city' => 'New York', 'street' => '5th Avenue'],
['city' => 'Rome', 'street' => 'Via della Conciliazione'],
];
$templateProcessor = new TestableTemplateProcesor($mainPart);
$templateProcessor->setMacroChars('{{', '}}');
$templateProcessor->cloneBlock('CLONEME', 0, true, false, $replacements);
self::assertStringContainsString('City: London, Street: Baker Street', $templateProcessor->getMainPart());
self::assertStringContainsString('City: New York, Street: 5th Avenue', $templateProcessor->getMainPart());
self::assertStringContainsString('City: Rome, Street: Via della Conciliazione', $templateProcessor->getMainPart());
}
/** /**
* Template macros can be fixed. * Template macros can be fixed.
* *
@ -1114,38 +698,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals('<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>${variable_name}</w:t></w:r>', $fixed); self::assertEquals('<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>${variable_name}</w:t></w:r>', $fixed);
} }
/**
* Template macros can be fixed even with cutome macro.
*
* @covers ::fixBrokenMacros
*/
public function testFixBrokenMacrosWithCustomMacro(): void
{
$templateProcessor = new TestableTemplateProcesor();
$templateProcessor->setMacroChars('{{', '}}');
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>normal text</w:t></w:r>');
self::assertEquals('<w:r><w:t>normal text</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>{{documentContent}}</w:t></w:r>');
self::assertEquals('<w:r><w:t>{{documentContent}}</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>{</w:t><w:t>{documentContent}}</w:t></w:r>');
self::assertEquals('<w:r><w:t>{{documentContent}}</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>$1500</w:t><w:t>{{documentContent}}</w:t></w:r>');
self::assertEquals('<w:r><w:t>$1500</w:t><w:t>{{documentContent}}</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>$1500</w:t><w:t>{</w:t><w:t>{documentContent}}</w:t></w:r>');
self::assertEquals('<w:r><w:t>$1500</w:t><w:t>{{documentContent}}</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<w:r><w:t>25$ plus some info {hint}</w:t></w:r>');
self::assertEquals('<w:r><w:t>25$ plus some info {hint}</w:t></w:r>', $fixed);
$fixed = $templateProcessor->fixBrokenMacros('<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>');
self::assertEquals('<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>{{variable_name}}</w:t></w:r>', $fixed);
}
/** /**
* @covers ::getMainPartName * @covers ::getMainPartName
*/ */
@ -1158,19 +710,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals($variables, $templateProcessor->getVariables()); self::assertEquals($variables, $templateProcessor->getVariables());
} }
/**
* @covers ::getMainPartName
*/
public function testMainPartNameDetectionWithCustomMacro(): void
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/document22-with-custom-macro-xml.docx');
$templateProcessor->setMacroOpeningChars('{#');
$templateProcessor->setMacroClosingChars('#}');
$variables = ['test'];
self::assertEquals($variables, $templateProcessor->getVariables());
}
/** /**
* @covers ::getVariables * @covers ::getVariables
*/ */
@ -1188,25 +727,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals(['variable_name'], $variables); self::assertEquals(['variable_name'], $variables);
} }
/**
* @covers ::getVariables
*/
public function testGetVariablesWithCustomMacro(): void
{
$templateProcessor = new TestableTemplateProcesor();
$templateProcessor->setMacroOpeningChars('{{');
$templateProcessor->setMacroClosingChars('}}');
$variables = $templateProcessor->getVariablesForPart('<w:r><w:t>normal text</w:t></w:r>');
self::assertEquals([], $variables);
$variables = $templateProcessor->getVariablesForPart('<w:r><w:t>{{documentContent}}</w:t></w:r>');
self::assertEquals(['documentContent'], $variables);
$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>');
self::assertEquals(['variable_name'], $variables);
}
/** /**
* @covers ::textNeedsSplitting * @covers ::textNeedsSplitting
*/ */
@ -1222,22 +742,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertFalse($templateProcessor->textNeedsSplitting($splitText)); self::assertFalse($templateProcessor->textNeedsSplitting($splitText));
} }
/**
* @covers ::textNeedsSplitting
*/
public function testTextNeedsSplittingWithCustomMacro(): void
{
$templateProcessor = new TestableTemplateProcesor();
$templateProcessor->setMacroChars('{{', '}}');
self::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>';
self::assertTrue($templateProcessor->textNeedsSplitting($text));
$splitText = $templateProcessor->splitTextIntoTexts($text);
self::assertFalse($templateProcessor->textNeedsSplitting($splitText));
}
/** /**
* @covers ::splitTextIntoTexts * @covers ::splitTextIntoTexts
*/ */
@ -1252,21 +756,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::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); self::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);
} }
/**
* @covers ::splitTextIntoTexts
*/
public function testSplitTextIntoTextsWithCustomMacro(): void
{
$templateProcessor = new TestableTemplateProcesor();
$templateProcessor->setMacroChars('{{', '}}');
$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>');
self::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>');
self::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(): void public function testFindXmlBlockStart(): void
{ {
$toFind = '<w:r> $toFind = '<w:r>
@ -1310,50 +799,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertEquals($toFind, $templateProcessor->getSlice($position['start'], $position['end'])); self::assertEquals($toFind, $templateProcessor->getSlice($position['start'], $position['end']));
} }
public function testFindXmlBlockStartWithCustomMacro(): void
{
$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);
$templateProcessor->setMacroChars('{{', '}}');
$position = $templateProcessor->findContainingXmlBlockForMacro('{{title}}', 'w:r');
self::assertEquals($toFind, $templateProcessor->getSlice($position['start'], $position['end']));
}
public function testShouldReturnFalseIfXmlBlockNotFound(): void public function testShouldReturnFalseIfXmlBlockNotFound(): void
{ {
$mainPart = '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> $mainPart = '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
@ -1381,34 +826,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
self::assertFalse($result); self::assertFalse($result);
} }
public function testShouldReturnFalseIfXmlBlockNotFoundWithCustomMacro(): void
{
$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);
$templateProcessor->setMacroChars('{{', '}}');
//non-existing macro
$result = $templateProcessor->findContainingXmlBlockForMacro('{{fake-macro}}', 'w:p');
self::assertFalse($result);
//existing macro but not inside node looked for
$result = $templateProcessor->findContainingXmlBlockForMacro('{{macro}}', 'w:fake-node');
self::assertFalse($result);
//existing macro but end tag not found after macro
$result = $templateProcessor->findContainingXmlBlockForMacro('{{macro}}', 'w:rPr');
self::assertFalse($result);
}
public function testShouldMakeFieldsUpdateOnOpen(): void public function testShouldMakeFieldsUpdateOnOpen(): void
{ {
$settingsPart = '<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> $settingsPart = '<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
@ -1422,19 +839,4 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
$templateProcessor->setUpdateFields(false); $templateProcessor->setUpdateFields(false);
self::assertStringContainsString('<w:updateFields w:val="false"/>', $templateProcessor->getSettingsPart()); self::assertStringContainsString('<w:updateFields w:val="false"/>', $templateProcessor->getSettingsPart());
} }
public function testShouldMakeFieldsUpdateOnOpenWithCustomMacro(): void
{
$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->setMacroChars('{{', '}}');
$templateProcessor->setUpdateFields(true);
self::assertStringContainsString('<w:updateFields w:val="true"/>', $templateProcessor->getSettingsPart());
$templateProcessor->setUpdateFields(false);
self::assertStringContainsString('<w:updateFields w:val="false"/>', $templateProcessor->getSettingsPart());
}
} }

View File

@ -2,8 +2,10 @@
/** /**
* This file is part of PHPWord - A pure PHP library for reading and writing * This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents. * word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser * PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation. * General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE * For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of * file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
@ -59,11 +61,11 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
$doc = TestHelperDOCX::getDocument($phpWord); $doc = TestHelperDOCX::getDocument($phpWord);
self::assertNotNull($doc); self::assertNotNull($doc);
// $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr')); // $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool')); // $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key3"]/vt:i4')); // $this->assertTrue($doc->elementExists('/Properties/property[name="key3"]/vt:i4'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key4"]/vt:r8')); // $this->assertTrue($doc->elementExists('/Properties/property[name="key4"]/vt:r8'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key5"]/vt:lpwstr')); // $this->assertTrue($doc->elementExists('/Properties/property[name="key5"]/vt:lpwstr'));
} }
/** /**
@ -406,13 +408,7 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
// behind // behind
$element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:pict/v:shape'); $element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:pict/v:shape');
$style = $element->getAttribute('style'); $style = $element->getAttribute('style');
self::assertRegExp('/z\-index:\-[0-9]*/', $style);
// Try to address CI coverage issue for PHP 7.1 and 7.2 when using regex match assertions
if (method_exists(static::class, 'assertRegExp')) {
self::assertRegExp('/z\-index:\-[0-9]*/', $style);
} else {
self::assertMatchesRegularExpression('/z\-index:\-[0-9]*/', $style);
}
// square // square
$element = $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:pict/v:shape/w10:wrap'); $element = $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:pict/v:shape/w10:wrap');
@ -555,13 +551,7 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
$cell->addText('Test'); $cell->addText('Test');
$doc = TestHelperDOCX::getDocument($phpWord); $doc = TestHelperDOCX::getDocument($phpWord);
self::assertEquals( self::assertEquals(Cell::DEFAULT_BORDER_COLOR, $doc->getElementAttribute('/w:document/w:body/w:tbl/w:tr/w:tc/w:tcPr/w:tcBorders/w:top', 'w:color'));
Cell::DEFAULT_BORDER_COLOR,
$doc->getElementAttribute(
'/w:document/w:body/w:tbl/w:tr/w:tc/w:tcPr/w:tcBorders/w:top',
'w:color'
)
);
} }
/** /**