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
/.scrutinizer.yml export-ignore
/.github export-ignore
/.travis.yml export-ignore
/php_cs.dist export-ignore
/phpmd.xml.dist export-ignore
/phpstan.neon export-ignore
@ -18,4 +18,4 @@
# tests
/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/).
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).

View File

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

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions
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")
Master:
[![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 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)
[![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)
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 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)
@ -71,9 +78,9 @@ Run the following to use the latest stable version
```sh
composer require phpoffice/phpword
```
or if you want the latest unreleased version
or if you want the latest develop version
```sh
composer require phpoffice/phpword:dev-master
composer require phpoffice/phpword:dev-develop
```
## 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.
- 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.
- Follow [@PHPWord](https://twitter.com/PHPWord) and [@PHPOffice](https://twitter.com/PHPOffice) on Twitter.

View File

@ -70,11 +70,11 @@
"ext-libxml": "*",
"dompdf/dompdf": "^2.0",
"mpdf/mpdf": "^8.1",
"php-coveralls/php-coveralls": "^2.5",
"phpmd/phpmd": "^2.13",
"phpunit/phpunit": ">=7.0",
"tecnickcom/tcpdf": "^6.5",
"symfony/process": "^4.4",
"friendsofphp/php-cs-fixer": "^3.3"
"symfony/process": "^4.4"
},
"suggest": {
"ext-zip": "Allows writing OOXML and ODF",
@ -92,5 +92,10 @@
"psr-4": {
"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
$proofState = new \PhpOffice\PhpWord\ComplexType\ProofState();
$proofState->setGrammar(\PhpOffice\PhpWord\ComplexType\ProofState::CLEAN);
$proofState->setSpelling(\PhpOffice\PhpWord\ComplexType\ProofState::DIRTY);
$proofState = new ProofState();
$proofState->setGrammar(ProofState::CLEAN);
$proofState->setSpelling(ProofState::DIRTY);
$phpWord->getSettings()->setProofState($proofState);
$phpWord->getSettings()->setProofState(proofState);
Track Revisions
~~~~~~~~~~~~~~~

View File

@ -39,7 +39,7 @@ Example:
.. code-block:: bash
composer require phpoffice/phpword:dev-master
composer require phpoffice/phpword:dev-develop
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).
PHPWord is an open source project licensed under the terms of `LGPL
version 3 <https://github.com/PHPOffice/PHPWord/blob/master/COPYING.LESSER>`__.
PHPWord is aimed to be a high quality software product.
version 3 <https://github.com/PHPOffice/PHPWord/blob/develop/COPYING.LESSER>`__.
PHPWord is aimed to be a high quality software product by incorporating
`continuous integration <https://travis-ci.org/PHPOffice/PHPWord>`__ and
`unit testing <http://phpoffice.github.io/PHPWord/coverage/develop/>`__.
You can learn more about PHPWord by reading this Developers'
Documentation.
Documentation and the `API
Documentation <http://phpoffice.github.io/PHPWord/docs/develop/>`__.
Features
--------
@ -188,7 +191,7 @@ things that you can do to contribute.
guide <https://github.com/PHPOffice/PHPWord/blob/master/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>`__
`develop <https://github.com/PHPOffice/PHPWord/tree/develop>`__
branch.
- Submit `bug reports or feature
requests <https://github.com/PHPOffice/PHPWord/issues>`__ to GitHub.

View File

@ -29,7 +29,7 @@ Use ``php://output`` as the filename.
.. code-block:: php
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
$section = $phpWord->createSection();
$section->addText('Hello World!');
$file = 'HelloWorld.docx';
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.
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.
.. 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'));
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
"""""""""""""
The search-pattern model for images can be like:

View File

@ -323,8 +323,6 @@ abstract class AbstractPart
$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'],
'textDirection' => [self::READ_VALUE, 'w:textDirection'],
'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'],
];
@ -628,7 +626,7 @@ abstract class AbstractPart
$styles = [];
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);
if ($element === null) {
@ -642,7 +640,7 @@ abstract class AbstractPart
// Use w:val as default if no attribute assigned
$attribute = ($attribute === null) ? 'w:val' : $attribute;
$attributeValue = $xmlReader->getAttribute($attribute, $node) ?? $default;
$attributeValue = $xmlReader->getAttribute($attribute, $node);
$styleValue = $this->readStyleDef($method, $attributeValue, $expected);
if ($styleValue !== null) {

View File

@ -2,8 +2,10 @@
/**
* 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.
@ -27,46 +29,48 @@ class Settings
*
* @const string
*/
public const ZIPARCHIVE = 'ZipArchive';
public const PCLZIP = 'PclZip';
public const OLD_LIB = \PhpOffice\PhpWord\Shared\ZipArchive::class; // @deprecated 0.11
const ZIPARCHIVE = 'ZipArchive';
const PCLZIP = 'PclZip';
const OLD_LIB = \PhpOffice\PhpWord\Shared\ZipArchive::class; // @deprecated 0.11
/**
* PDF rendering libraries.
*
* @const string
*/
public const PDF_RENDERER_DOMPDF = 'DomPDF';
public const PDF_RENDERER_TCPDF = 'TCPDF';
public const PDF_RENDERER_MPDF = 'MPDF';
const PDF_RENDERER_DOMPDF = 'DomPDF';
const PDF_RENDERER_TCPDF = 'TCPDF';
const PDF_RENDERER_MPDF = 'MPDF';
/**
* Measurement units multiplication factor.
*
* Applied to:
* - Section: margins, header/footer height, gutter, column spacing
* - Tab: position
* - Indentation: left, right, firstLine, hanging
* - Spacing: before, after.
* - Spacing: before, after
*
* @const string
*/
public const UNIT_TWIP = 'twip'; // = 1/20 point
public const UNIT_CM = 'cm';
public const UNIT_MM = 'mm';
public const UNIT_INCH = 'inch';
public const UNIT_POINT = 'point'; // = 1/72 inch
public const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points
const UNIT_TWIP = 'twip'; // = 1/20 point
const UNIT_CM = 'cm';
const UNIT_MM = 'mm';
const UNIT_INCH = 'inch';
const UNIT_POINT = 'point'; // = 1/72 inch
const UNIT_PICA = 'pica'; // = 1/6 inch = 12 points
/**
* Default font settings.
*
* OOXML defined font size values in halfpoints, i.e. twice of what PhpWord
* use, and the conversion will be conducted during XML writing.
*/
public const DEFAULT_FONT_NAME = 'Arial';
public const DEFAULT_FONT_SIZE = 10;
public const DEFAULT_FONT_COLOR = '000000';
public const DEFAULT_FONT_CONTENT_TYPE = 'default'; // default|eastAsia|cs
public const DEFAULT_PAPER = 'A4';
const DEFAULT_FONT_NAME = 'Arial';
const DEFAULT_FONT_SIZE = 10;
const DEFAULT_FONT_COLOR = '000000';
const DEFAULT_FONT_CONTENT_TYPE = 'default'; // default|eastAsia|cs
const DEFAULT_PAPER = 'A4';
/**
* Compatibility option for XMLWriter.
@ -85,21 +89,21 @@ class Settings
/**
* Name of the external Library used for rendering PDF files.
*
* @var null|string
* @var string
*/
private static $pdfRendererName;
/**
* Directory Path to the external Library used for rendering PDF files.
*
* @var null|string
* @var string
*/
private static $pdfRendererPath;
/**
* Measurement unit.
*
* @var string
* @var float|int
*/
private static $measurementUnit = self::UNIT_TWIP;
@ -113,7 +117,7 @@ class Settings
/**
* Default font size.
*
* @var float|int
* @var int
*/
private static $defaultFontSize = self::DEFAULT_FONT_SIZE;
@ -144,17 +148,23 @@ class Settings
*
* @return bool Compatibility
*/
public static function hasCompatibility(): bool
public static function hasCompatibility()
{
return self::$xmlWriterCompatibility;
}
/**
* 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;
return true;
@ -162,16 +172,22 @@ class Settings
/**
* Get zip handler class.
*
* @return string
*/
public static function getZipClass(): string
public static function getZipClass()
{
return self::$zipClass;
}
/**
* 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])) {
self::$zipClass = $zipClass;
@ -185,9 +201,12 @@ class Settings
/**
* Set details of the external library for rendering PDF files.
*
* @param string $libraryName
* @param string $libraryBaseDir
*
* @return bool Success or failure
*/
public static function setPdfRenderer(string $libraryName, string $libraryBaseDir): bool
public static function setPdfRenderer($libraryName, $libraryBaseDir)
{
if (!self::setPdfRendererName($libraryName)) {
return false;
@ -198,16 +217,22 @@ class Settings
/**
* Return the PDF Rendering Library.
*
* @return string
*/
public static function getPdfRendererName(): ?string
public static function getPdfRendererName()
{
return self::$pdfRendererName;
}
/**
* 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];
if (!in_array($libraryName, $pdfRenderers)) {
@ -220,8 +245,10 @@ class Settings
/**
* Return the directory path to the PDF Rendering Library.
*
* @return string
*/
public static function getPdfRendererPath(): ?string
public static function getPdfRendererPath()
{
return self::$pdfRendererPath;
}
@ -229,11 +256,11 @@ class Settings
/**
* 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
*/
public static function setPdfRendererPath(?string $libraryBaseDir): bool
public static function setPdfRendererPath($libraryBaseDir)
{
if (!$libraryBaseDir || false === file_exists($libraryBaseDir) || false === is_readable($libraryBaseDir)) {
return false;
@ -245,25 +272,25 @@ class Settings
/**
* Get measurement unit.
*
* @return string
*/
public static function getMeasurementUnit(): string
public static function getMeasurementUnit()
{
return self::$measurementUnit;
}
/**
* Set measurement unit.
*
* @param string $value
*
* @return bool
*/
public static function setMeasurementUnit(string $value): bool
public static function setMeasurementUnit($value)
{
$units = [
self::UNIT_TWIP,
self::UNIT_CM,
self::UNIT_MM,
self::UNIT_INCH,
self::UNIT_POINT,
self::UNIT_PICA,
];
$units = [self::UNIT_TWIP, self::UNIT_CM, self::UNIT_MM, self::UNIT_INCH,
self::UNIT_POINT, self::UNIT_PICA, ];
if (!in_array($value, $units)) {
return false;
}
@ -275,11 +302,11 @@ class Settings
/**
* Sets the user defined path to temporary directory.
*
* @param string $tempDir The user defined path to temporary directory
*
* @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;
}
@ -288,8 +315,10 @@ class Settings
* Returns path to temporary directory.
*
* @since 0.12.0
*
* @return string
*/
public static function getTempDir(): string
public static function getTempDir()
{
if (!empty(self::$tempDir)) {
$tempDir = self::$tempDir;
@ -302,34 +331,44 @@ class Settings
/**
* @since 0.13.0
*
* @return bool
*/
public static function isOutputEscapingEnabled(): bool
public static function isOutputEscapingEnabled()
{
return self::$outputEscapingEnabled;
}
/**
* @since 0.13.0
*
* @param bool $outputEscapingEnabled
*/
public static function setOutputEscapingEnabled(bool $outputEscapingEnabled): void
public static function setOutputEscapingEnabled($outputEscapingEnabled): void
{
self::$outputEscapingEnabled = $outputEscapingEnabled;
}
/**
* Get default font name.
*
* @return string
*/
public static function getDefaultFontName(): string
public static function getDefaultFontName()
{
return self::$defaultFontName;
}
/**
* 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;
return true;
@ -341,7 +380,7 @@ class Settings
/**
* Get default font size.
*
* @return float|int
* @return int
*/
public static function getDefaultFontSize()
{
@ -351,11 +390,14 @@ class Settings
/**
* 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;
return true;
@ -366,8 +408,12 @@ class Settings
/**
* 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
$configFile = null;
@ -409,18 +455,24 @@ class Settings
/**
* Get default paper.
*
* @return string
*/
public static function getDefaultPaper(): string
public static function getDefaultPaper()
{
return self::$defaultPaper;
}
/**
* 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;
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;
/**
* @var Css
*/
protected static $css;
/**
* 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,
* 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,
// fix ampersand and angle brackets and add body tag for HTML fragments
@ -87,10 +82,10 @@ class Html
$dom = new DOMDocument();
$dom->preserveWhiteSpace = $preserveWhiteSpace;
$dom->loadXML($html);
static::$xpath = new DOMXPath($dom);
self::$xpath = new DOMXPath($dom);
$node = $dom->getElementsByTagName('body');
static::parseNode($node->item(0), $element);
self::parseNode($node->item(0), $element);
if (\PHP_VERSION_ID < 80000) {
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');
if ($attributeStyle) {
$styles = self::parseStyle($attributeStyle, $styles);
@ -186,13 +168,6 @@ class Html
*/
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
$styleTypes = ['font', 'paragraph', 'list', 'table', 'row', 'cell'];
foreach ($styleTypes as $styleType) {
@ -414,11 +389,6 @@ class Html
$newElement = $element->addTable($elementStyles);
// Add style name from CSS Class
if (isset($elementStyles['className'])) {
$newElement->getStyle()->setStyleName($elementStyles['className']);
}
$attributes = $node->attributes;
if ($attributes->getNamedItem('border') !== null) {
$border = (int) $attributes->getNamedItem('border')->value;
@ -444,11 +414,7 @@ class Html
$rowStyles['tblHeader'] = true;
}
// set cell height to control row heights
$height = $rowStyles['height'] ?? null;
unset($rowStyles['height']); // would not apply
return $element->addRow($height, $rowStyles);
return $element->addRow(null, $rowStyles);
}
/**
@ -669,21 +635,13 @@ class Html
{
$properties = explode(';', trim($attribute->value, " \t\n\r\0\x0B;"));
$selectors = [];
foreach ($properties as $property) {
[$cKey, $cValue] = array_pad(explode(':', $property, 2), 2, null);
$selectors[strtolower(trim($cKey))] = trim($cValue ?? '');
}
return self::parseStyleDeclarations($selectors, $styles);
}
protected static function parseStyleDeclarations(array $selectors, array $styles)
{
foreach ($selectors as $property => $value) {
switch ($property) {
$cValue = trim($cValue ?? '');
$cKey = strtolower(trim($cKey));
switch ($cKey) {
case 'text-decoration':
switch ($value) {
switch ($cValue) {
case 'underline':
$styles['underline'] = 'single';
@ -696,44 +654,44 @@ class Html
break;
case 'text-align':
$styles['alignment'] = self::mapAlign($value);
$styles['alignment'] = self::mapAlign($cValue);
break;
case 'display':
$styles['hidden'] = $value === 'none' || $value === 'hidden';
$styles['hidden'] = $cValue === 'none' || $cValue === 'hidden';
break;
case 'direction':
$styles['rtl'] = $value === 'rtl';
$styles['rtl'] = $cValue === 'rtl';
break;
case 'font-size':
$styles['size'] = Converter::cssToPoint($value);
$styles['size'] = Converter::cssToPoint($cValue);
break;
case 'font-family':
$value = array_map('trim', explode(',', $value));
$styles['name'] = ucwords($value[0]);
$cValue = array_map('trim', explode(',', $cValue));
$styles['name'] = ucwords($cValue[0]);
break;
case 'color':
$styles['color'] = trim($value, '#');
$styles['color'] = trim($cValue, '#');
break;
case 'background-color':
$styles['bgColor'] = trim($value, '#');
$styles['bgColor'] = trim($cValue, '#');
break;
case 'line-height':
$matches = [];
if ($value === 'normal') {
if ($cValue === 'normal') {
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
$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, ...
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::EXACT;
$spacing = Converter::cssToTwip($matches[1]);
} elseif (preg_match('/([0-9]+)%/', $value, $matches)) {
} elseif (preg_match('/([0-9]+)%/', $cValue, $matches)) {
//matches percentages
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
//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
$spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO;
//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['line-spacing'] = $spacing;
break;
case 'letter-spacing':
$styles['letter-spacing'] = Converter::cssToTwip($value);
$styles['letter-spacing'] = Converter::cssToTwip($cValue);
break;
case 'text-indent':
$styles['indentation']['firstLine'] = Converter::cssToTwip($value);
$styles['indentation']['firstLine'] = Converter::cssToTwip($cValue);
break;
case 'font-weight':
$tValue = false;
if (preg_match('#bold#', $value)) {
if (preg_match('#bold#', $cValue)) {
$tValue = true; // also match bolder
}
$styles['bold'] = $tValue;
@ -766,57 +724,52 @@ class Html
break;
case 'font-style':
$tValue = false;
if (preg_match('#(?:italic|oblique)#', $value)) {
if (preg_match('#(?:italic|oblique)#', $cValue)) {
$tValue = true;
}
$styles['italic'] = $tValue;
break;
case 'margin':
$value = Converter::cssToTwip($value);
$styles['spaceBefore'] = $value;
$styles['spaceAfter'] = $value;
$cValue = Converter::cssToTwip($cValue);
$styles['spaceBefore'] = $cValue;
$styles['spaceAfter'] = $cValue;
break;
case 'margin-top':
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value)
$styles['spaceBefore'] = Converter::cssToTwip($value);
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue)
$styles['spaceBefore'] = Converter::cssToTwip($cValue);
break;
case 'margin-bottom':
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value)
$styles['spaceAfter'] = Converter::cssToTwip($value);
// BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue)
$styles['spaceAfter'] = Converter::cssToTwip($cValue);
break;
case 'border-color':
self::mapBorderColor($styles, $value);
self::mapBorderColor($styles, $cValue);
break;
case 'border-width':
$styles['borderSize'] = Converter::cssToPoint($value);
$styles['borderSize'] = Converter::cssToPoint($cValue);
break;
case 'border-style':
$styles['borderStyle'] = self::mapBorderStyle($value);
$styles['borderStyle'] = self::mapBorderStyle($cValue);
break;
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['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['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['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::AUTO;
}
break;
case 'height':
$styles['height'] = Converter::cssToTwip($value);
$styles['exactHeight'] = true;
break;
case 'border':
case 'border-top':
@ -825,9 +778,9 @@ class Html
case 'border-left':
// 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
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $value, $matches)) {
if (false !== strpos($property, '-')) {
$tmp = explode('-', $property);
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $cValue, $matches)) {
if (false !== strpos($cKey, '-')) {
$tmp = explode('-', $cKey);
$which = $tmp[1];
$which = ucfirst($which); // e.g. bottom -> Bottom
} else {
@ -850,13 +803,13 @@ class Html
break;
case '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]);
}
break;
case 'page-break-after':
if ($value == 'always') {
if ($cValue == 'always') {
$styles['isPageBreak'] = true;
}

View File

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

View File

@ -70,9 +70,6 @@ final class Language extends AbstractStyle
const NL_NL = 'nl-NL';
const NL_NL_ID = 1043;
const SV_SE = 'sv-SE';
const SV_SE_ID = 1053;
const UK_UA = 'uk-UA';
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
* 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.
@ -25,128 +27,121 @@ class TextBox extends Image
/**
* margin top.
*
* @var null|int
* @var int
*/
private $innerMarginTop;
/**
* margin left.
*
* @var null|int
* @var int
*/
private $innerMarginLeft;
/**
* margin right.
*
* @var null|int
* @var int
*/
private $innerMarginRight;
/**
* Cell margin bottom.
*
* @var null|int
* @var int
*/
private $innerMarginBottom;
/**
* border size.
*
* @var null|int
* @var int
*/
private $borderSize;
/**
* border color.
*
* @var null|string
* @var string
*/
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.
*
* @param int $value
*/
public function setInnerMarginTop(?int $value = null): void
public function setInnerMarginTop($value = null): void
{
$this->innerMarginTop = $value;
}
/**
* Get margin top.
*
* @return int
*/
public function getInnerMarginTop(): ?int
public function getInnerMarginTop()
{
return $this->innerMarginTop;
}
/**
* Set margin left.
*
* @param int $value
*/
public function setInnerMarginLeft(?int $value = null): void
public function setInnerMarginLeft($value = null): void
{
$this->innerMarginLeft = $value;
}
/**
* Get margin left.
*
* @return int
*/
public function getInnerMarginLeft(): ?int
public function getInnerMarginLeft()
{
return $this->innerMarginLeft;
}
/**
* Set margin right.
*
* @param int $value
*/
public function setInnerMarginRight(?int $value = null): void
public function setInnerMarginRight($value = null): void
{
$this->innerMarginRight = $value;
}
/**
* Get margin right.
*
* @return int
*/
public function getInnerMarginRight(): ?int
public function getInnerMarginRight()
{
return $this->innerMarginRight;
}
/**
* Set margin bottom.
*
* @param int $value
*/
public function setInnerMarginBottom(?int $value = null): void
public function setInnerMarginBottom($value = null): void
{
$this->innerMarginBottom = $value;
}
/**
* Get margin bottom.
*
* @return int
*/
public function getInnerMarginBottom(): ?int
public function getInnerMarginBottom()
{
return $this->innerMarginBottom;
}
@ -154,9 +149,9 @@ class TextBox extends Image
/**
* 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->setInnerMarginLeft($value);
@ -169,15 +164,17 @@ class TextBox extends Image
*
* @return int[]
*/
public function getInnerMargin(): array
public function getInnerMargin()
{
return [$this->innerMarginLeft, $this->innerMarginTop, $this->innerMarginRight, $this->innerMarginBottom];
}
/**
* Has inner margin?
*
* @return bool
*/
public function hasInnerMargins(): bool
public function hasInnerMargins()
{
$hasInnerMargins = false;
$margins = $this->getInnerMargin();
@ -194,33 +191,39 @@ class TextBox extends Image
/**
* 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;
}
/**
* Get border size.
*
* @return int
*/
public function getBorderSize(): ?int
public function getBorderSize()
{
return $this->borderSize;
}
/**
* Set border color.
*
* @param string $value
*/
public function setBorderColor(?string $value = null): void
public function setBorderColor($value = null): void
{
$this->borderColor = $value;
}
/**
* Get border color.
*
* @return string
*/
public function getBorderColor(): ?string
public function getBorderColor()
{
return $this->borderColor;
}

View File

@ -94,10 +94,6 @@ class TemplateProcessor
*/
protected $tempDocumentNewImages = [];
protected static $macroOpeningChars = '${';
protected static $macroClosingChars = '}';
/**
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception
*
@ -242,8 +238,8 @@ class TemplateProcessor
*/
protected static function ensureMacroCompleted($macro)
{
if (substr($macro, 0, 2) !== self::$macroOpeningChars && substr($macro, -1) !== self::$macroClosingChars) {
$macro = self::$macroOpeningChars . $macro . self::$macroClosingChars;
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
$macro = '${' . $macro . '}';
}
return $macro;
@ -763,70 +759,6 @@ class TemplateProcessor
$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.
*
@ -860,12 +792,8 @@ class TemplateProcessor
{
$xmlBlock = null;
$matches = [];
$escapedMacroOpeningChars = self::$macroOpeningChars;
$escapedMacroClosingChars = self::$macroClosingChars;
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).)*?\\' . $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',
'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\${' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart,
$matches
);
@ -904,10 +832,8 @@ class TemplateProcessor
public function replaceBlock($blockname, $replacement): void
{
$matches = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
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,
$matches
);
@ -1023,12 +949,8 @@ class TemplateProcessor
*/
protected function fixBrokenMacros($documentPart)
{
$brokenMacroOpeningChars = substr(self::$macroOpeningChars, 0, 1);
$endMacroOpeningChars = substr(self::$macroOpeningChars, 1);
$macroClosingChars = self::$macroClosingChars;
return preg_replace_callback(
'/\\' . $brokenMacroOpeningChars . '(?:\\' . $endMacroOpeningChars . '|[^{$]*\>\{)[^' . $macroClosingChars . '$]*\}/U',
'/\$(?:\{|[^{$]*\>\{)[^}$]*\}/U',
function ($match) {
return strip_tags($match[0]);
},
@ -1041,7 +963,7 @@ class TemplateProcessor
*
* @param mixed $search
* @param mixed $replace
* @param array<int, string>|string $documentPartXML
* @param string $documentPartXML
* @param int $limit
*
* @return string
@ -1067,10 +989,7 @@ class TemplateProcessor
protected function getVariablesForPart($documentPartXML)
{
$matches = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
preg_match_all("/$escapedMacroOpeningChars(.*?)$escapedMacroClosingChars/i", $documentPartXML, $matches);
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
return $matches[1];
}
@ -1160,39 +1079,6 @@ class TemplateProcessor
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.
*
@ -1255,11 +1141,8 @@ class TemplateProcessor
protected function indexClonedVariables($count, $xmlBlock)
{
$results = [];
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
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;
@ -1414,7 +1297,7 @@ class TemplateProcessor
}
$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);
}
@ -1428,25 +1311,6 @@ class TemplateProcessor
*/
protected function textNeedsSplitting($text)
{
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
$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;
return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
}
}

View File

@ -2,8 +2,10 @@
/**
* 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.
@ -48,10 +50,6 @@ class TextBox extends Image
$xmlWriter->startElement('v:shape');
$xmlWriter->writeAttribute('type', '#_x0000_t0202');
if ($style->getBgColor()) {
$xmlWriter->writeAttribute('fillcolor', $style->getBgColor());
}
$styleWriter->write();
$styleWriter->writeBorder();

View File

@ -2,8 +2,10 @@
/**
* 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.

View File

@ -2,8 +2,10 @@
/**
* 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.
@ -17,15 +19,15 @@ namespace PhpOffice\PhpWordTests;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use PHPUnit\Framework\TestCase;
/**
* Test class for PhpOffice\PhpWord\Settings.
*
* @coversDefaultClass \PhpOffice\PhpWord\Settings
*
* @runTestsInSeparateProcesses
*/
class SettingsTest extends TestCase
class SettingsTest extends \PHPUnit\Framework\TestCase
{
private $compatibility;
@ -149,6 +151,7 @@ class SettingsTest extends TestCase
/**
* @covers ::getTempDir
* @covers ::setTempDir
*
* @depends testPhpTempDirIsUsedByDefault
*/
public function testTempDirCanBeSet(): void
@ -186,12 +189,6 @@ class SettingsTest extends TestCase
self::assertEquals(12, Settings::getDefaultFontSize());
self::assertFalse(Settings::setDefaultFontSize(null));
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 PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Shared\Html;
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);
}
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.
*/
@ -464,58 +425,6 @@ HTML;
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).
*/

View File

@ -2,8 +2,10 @@
/**
* 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.
@ -18,15 +20,15 @@ namespace PhpOffice\PhpWordTests\Style;
use InvalidArgumentException;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\TextBox;
use PHPUnit\Framework\TestCase;
/**
* Test class for PhpOffice\PhpWord\Style\Image.
*
* @coversDefaultClass \PhpOffice\PhpWord\Style\Image
*
* @runTestsInSeparateProcesses
*/
class TextBoxTest extends TestCase
class TextBoxTest extends \PHPUnit\Framework\TestCase
{
/**
* Test setting style with normal value.
@ -53,7 +55,6 @@ class TextBoxTest extends TestCase
'innerMarginLeft' => '5',
'borderSize' => '2',
'borderColor' => 'red',
'bgColor' => 'blue',
];
foreach ($properties as $key => $value) {
$set = "set{$key}";
@ -88,7 +89,6 @@ class TextBoxTest extends TestCase
'innerMarginLeft' => '5',
'borderSize' => '2',
'borderColor' => 'red',
'bgColor' => 'blue',
];
foreach ($properties as $key => $value) {
$get = "get{$key}";
@ -305,15 +305,4 @@ class TextBoxTest extends TestCase
$object->setBorderColor($expected);
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);
}
/**
* @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 ::saveAs
@ -232,33 +206,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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 ::saveAs
@ -328,68 +275,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
$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 ::setValue
@ -411,29 +296,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
*/
@ -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
{
$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()));
}
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
*/
@ -590,25 +382,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
*/
@ -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
*/
@ -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
*/
@ -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}'));
}
/**
* @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
*/
@ -984,38 +634,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
{
$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());
}
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.
*
@ -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);
}
/**
* 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
*/
@ -1158,19 +710,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
*/
@ -1188,25 +727,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
*/
@ -1222,22 +742,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
*/
@ -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);
}
/**
* @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
{
$toFind = '<w:r>
@ -1310,50 +799,6 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
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
{
$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);
}
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
{
$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);
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
* 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.
@ -59,11 +61,11 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
$doc = TestHelperDOCX::getDocument($phpWord);
self::assertNotNull($doc);
// $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key3"]/vt:i4'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key4"]/vt:r8'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key5"]/vt:lpwstr'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key1"]/vt:lpwstr'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key2"]/vt:bool'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key3"]/vt:i4'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key4"]/vt:r8'));
// $this->assertTrue($doc->elementExists('/Properties/property[name="key5"]/vt:lpwstr'));
}
/**
@ -406,13 +408,7 @@ class DocumentTest extends \PHPUnit\Framework\TestCase
// behind
$element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:pict/v:shape');
$style = $element->getAttribute('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);
}
self::assertRegExp('/z\-index:\-[0-9]*/', $style);
// square
$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');
$doc = TestHelperDOCX::getDocument($phpWord);
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'
)
);
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'));
}
/**