Compare commits

..

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

22 changed files with 217 additions and 905 deletions

View File

@ -79,11 +79,14 @@ jobs:
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
@ -96,7 +99,7 @@ jobs:
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

View File

@ -73,8 +73,7 @@
"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",

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

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

@ -66,7 +66,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 +87,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);
} }
@ -160,12 +160,9 @@ class Html
} }
$attributeClass = $attributes->getNamedItem('class'); $attributeClass = $attributes->getNamedItem('class');
if ($attributeClass) { if ($attributeClass && self::$css) {
if (self::$css) {
$styles = self::parseStyleDeclarations(self::$css->getStyle('.' . $attributeClass->value), $styles); $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) {
@ -414,11 +411,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 +436,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);
} }
/** /**
@ -812,11 +800,6 @@ class Html
$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':

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;
@ -860,12 +856,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 +896,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 +1013,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 +1027,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 +1053,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];
} }
@ -1255,11 +1238,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 +1394,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 +1408,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

@ -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;
@ -135,17 +134,6 @@ class HtmlTest extends AbstractWebServerEmbeddedTest
self::assertEquals('22.5', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz', 'w:val')); 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 +452,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

@ -232,33 +232,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 +301,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 +322,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 +337,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 +390,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 +408,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 +547,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 +600,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 +629,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 +660,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 +693,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 +724,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 +736,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 +753,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 +768,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 +782,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 +825,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 +852,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 +865,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');
// 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); 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'
)
);
} }
/** /**