diff --git a/.github/ISSUE_TEMPLATE/1_bug_report.yml b/.github/ISSUE_TEMPLATE/1_bug_report.yml
new file mode 100644
index 0000000000..ea335468a5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1_bug_report.yml
@@ -0,0 +1,65 @@
+name: 🐛 Bug Report
+description: Create a report to help improve PHPWord
+labels: [ "Bug Report" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ### ❗️ Read this before submitting your bug report:
+ - **Write in English/French.** Reports in all other languages will be closed.
+ - **Provide as much detail as possible**
+ - Attachments : Error logs, Screenshots, Document files (generated and expected).
+ - If the issue cannot be reproduced, it cannot be fixed.
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: Describe the bug and add attachments
+ description: What went wrong? If possible, add screenshots, error logs, document files (generated and expected) or screen recordings to help explain your problem.
+ validations:
+ required: true
+ - type: textarea
+ id: expected-behavior
+ attributes:
+ label: Expected behavior
+ description: A clear and concise description of what you expected to happen.
+ validations:
+ required: true
+ - type: textarea
+ id: steps-reproduce
+ attributes:
+ label: Steps to reproduce
+ description: Please provide a code sample that reproduces the issue.
+ placeholder: |
+ ```php
+ addSection();
+ $section->...
+ ```
+ validations:
+ required: true
+ - type: input
+ id: phpword-version
+ attributes:
+ label: PHPWord version(s) where the bug happened
+ placeholder: "e.g., 1.2.0 or master"
+ validations:
+ required: true
+ - type: input
+ id: php-version
+ attributes:
+ label: PHP version(s) where the bug happened
+ placeholder: "e.g., 7.1 or 8.2"
+ validations:
+ required: true
+ - type: checkboxes
+ attributes:
+ label: Priority
+ description: Funded tickets have a higher priority.
+ options:
+ - label: I want to crowdfund the bug fix (with [@algora-io](https://docs.algora.io/bounties/overview)) and fund a community developer.
+ required: false
+ - label: I want to pay the bug fix and fund a maintainer for that. (Contact @Progi1984)
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.yml b/.github/ISSUE_TEMPLATE/2_feature_request.yml
new file mode 100644
index 0000000000..bf3539c372
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/2_feature_request.yml
@@ -0,0 +1,35 @@
+name: 💡 Feature request
+description: Suggest an idea for this project
+labels: [ "Change Request" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ### ❗️ Read this before submitting your bug report:
+ - **Write in English/French.** Reports in all other languages will be closed.
+ - **Provide as much detail as possible**
+ - Attachments : Error logs, Screenshots, Document files (generated and expected).
+ - If the issue cannot be reproduced, it cannot be fixed.
+ - type: textarea
+ id: problem
+ attributes:
+ label: Describe the problem
+ description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+ validations:
+ required: true
+ - type: textarea
+ id: expected-behavior
+ attributes:
+ label: Describe the expected behavior
+ description: A clear and concise description of what you expected to happen. If possible, add screenshots, document files (expected).
+ validations:
+ required: true
+ - type: checkboxes
+ attributes:
+ label: Priority
+ description: Funded tickets have a higher priority.
+ options:
+ - label: I want to crowdfund the feature (with [@algora-io](https://docs.algora.io/bounties/overview)) and fund a community developer.
+ required: false
+ - label: I want to pay the feature and fund a maintainer for that. (Contact @Progi1984)
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index fcb3a65db1..0000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-name: Bug report
-about: Create a report to help improve PHPWord
-labels: Bug Report
-
----
-
-### Describe the Bug
-
-A clear and concise description of what the bug is.
-
-### Steps to Reproduce
-
-Please provide a code sample that reproduces the issue.
-
-```php
-addSection();
-$section->...
-```
-
-### Expected Behavior
-
-A clear and concise description of what you expected to happen.
-
-### Current Behavior
-
-What is the current behavior?
-
-### Context
-
-Please fill in your environment information:
-
-- PHP Version:
-- PHPWord Version:
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 171e8378e1..0000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-labels: Change Request
-
----
-
-### Is your feature request related to a problem? Please describe.
-
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-### Describe the solution you'd like
-
-A clear and concise description of what you want to happen.
-
-### Describe alternatives you've considered
-
-A clear and concise description of any alternative solutions or features you've considered.
-
-### Additional context
-
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/how-to-use.md b/.github/ISSUE_TEMPLATE/how-to-use.md
deleted file mode 100644
index 85cc47072e..0000000000
--- a/.github/ISSUE_TEMPLATE/how-to-use.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-name: How to Use PHPWord
-about: Find out how to use PHPWord
-labels: WontFix
-
----
-
-***Please do not use the issue tracker to ask how to use PHPWord.***
-
-Documentation is available on [Read the Docs](https://phpword.readthedocs.io/en/latest/).
-
-Sample code is in the [`/samples/` directory](https://github.com/PHPOffice/PHPWord/tree/master/samples).
-
-Usage questions belong on [Stack Overflow](https://stackoverflow.com/questions/tagged/phpword).
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 5430a996ec..ce201f8bb6 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,6 +6,7 @@ Fixes # (issue)
### Checklist:
-- [ ] I have run `composer run-script check --timeout=0` and no errors were reported
-- [ ] The new code is covered by unit tests (check build/coverage for coverage report)
-- [ ] I have updated the documentation to describe the changes
+- [ ] My CI is :green_circle:
+- [ ] I have covered by unit tests my new code (check build/coverage for coverage report)
+- [ ] I have updated the [documentation](https://github.com/PHPOffice/PHPWord/tree/master/docs) to describe the changes
+- [ ] I have updated the [changelog](https://github.com/PHPOffice/PHPWord/blob/master/docs/changes/2.x/2.0.0.md)
diff --git a/docs/changes/1.x/1.2.0.md b/docs/changes/1.x/1.2.0.md
index 265d25b033..7a4b09ea2d 100644
--- a/docs/changes/1.x/1.2.0.md
+++ b/docs/changes/1.x/1.2.0.md
@@ -64,4 +64,5 @@
### BC Breaks
-- Removed dependency `laminas/laminas-escaper`
\ No newline at end of file
+- Removed dependency `laminas/laminas-escaper`
+- *Unintended Break* TemplateProcessor Does Not Persist File After Destruct. [#2539](https://github.com/PHPOffice/PHPWord/issues/2539) To be fixed by [#2545](https://github.com/PHPOffice/PHPWord/pull/2545
diff --git a/docs/changes/2.x/2.0.0.md b/docs/changes/2.x/2.0.0.md
index d534608be6..6f10bb171e 100644
--- a/docs/changes/2.x/2.0.0.md
+++ b/docs/changes/2.x/2.0.0.md
@@ -4,11 +4,16 @@
## Enhancements
+- IOFactory : Added extractVariables method to extract variables from a document [@sibalonat](https://github.com/sibalonat) in [#2515](https://github.com/PHPOffice/PHPWord/pull/2515)
+
### Bug fixes
- MsDoc Reader : Correct Font Size Calculation by [@oleibman](https://github.com/oleibman) fixing [#2526](https://github.com/PHPOffice/PHPWord/issues/2526) in [#2531](https://github.com/PHPOffice/PHPWord/pull/2531)
-- TemplateProcessor Persist File After Destruct [@oleibman](https://github.com/oleibman) fixing [#2539](https://github.com/PHPOffice/PHPWord/issues/2539) in [#2542](https://github.com/PHPOffice/PHPWord/pull/2531)
- Html Reader : Process Titles as Headings not Paragraphs [@0b10011](https://github.com/0b10011) and [@oleibman](https://github.com/oleibman) Issue [#1692](https://github.com/PHPOffice/PHPWord/issues/1692) PR [#2533](https://github.com/PHPOffice/PHPWord/pull/2533)
+- TemplateProcessor Persist File After Destruct [@oleibman](https://github.com/oleibman) fixing [#2539](https://github.com/PHPOffice/PHPWord/issues/2539) in [#2545](https://github.com/PHPOffice/PHPWord/pull/2545)
+- bug: TemplateProcessor fix multiline values [@gimler](https://github.com/gimler) fixing [#268](https://github.com/PHPOffice/PHPWord/issues/268), [#2323](https://github.com/PHPOffice/PHPWord/issues/2323) and [#2486](https://github.com/PHPOffice/PHPWord/issues/2486) in [#2522](https://github.com/PHPOffice/PHPWord/pull/2522)
+
+- 32-bit Problem in PasswordEncoder [@oleibman](https://github.com/oleibman) fixing [#2550](https://github.com/PHPOffice/PHPWord/issues/2550) in [#2551](https://github.com/PHPOffice/PHPWord/pull/2551)
### Miscellaneous
@@ -18,6 +23,7 @@
- Bump phpmd/phpmd from 2.14.1 to 2.15.0 by [@dependabot](https://github.com/dependabot) in [#2538](https://github.com/PHPOffice/PHPWord/pull/2538)
- Bump phpunit/phpunit from 9.6.14 to 9.6.15 by [@dependabot](https://github.com/dependabot) in [#2537](https://github.com/PHPOffice/PHPWord/pull/2537)
- Bump symfony/process from 5.4.28 to 5.4.34 by [@dependabot](https://github.com/dependabot) in [#2536](https://github.com/PHPOffice/PHPWord/pull/2536)
-- Allow rgb() when converting Html [@oleibman](https://github.com/oleibman) fixing [#2508](https://github.com/PHPOffice/PHPWord/issues/2508) in [#2512](https://github.com/PHPOffice/PHPWord/pull/2512)
+- Allow rgb() when converting Html by [@oleibman](https://github.com/oleibman) fixing [#2508](https://github.com/PHPOffice/PHPWord/issues/2508) in [#2512](https://github.com/PHPOffice/PHPWord/pull/2512)
+- Improved Issue Template by [@Progi1984](https://github.com/Progi1984) in [#2609](https://github.com/PHPOffice/PHPWord/pull/2609)
### BC Breaks
diff --git a/samples/Sample_44_ExtractVariablesFromReaderWord2007.php b/samples/Sample_44_ExtractVariablesFromReaderWord2007.php
new file mode 100644
index 0000000000..24574e5fb7
--- /dev/null
+++ b/samples/Sample_44_ExtractVariablesFromReaderWord2007.php
@@ -0,0 +1,14 @@
+load($filename);
}
+ /**
+ * Loads PhpWord ${variable} from file.
+ *
+ * @param string $filename The name of the file
+ *
+ * @return array The extracted variables
+ */
+ public static function extractVariables(string $filename, string $readerName = 'Word2007'): array
+ {
+ /** @var \PhpOffice\PhpWord\Reader\ReaderInterface $reader */
+ $reader = self::createReader($readerName);
+ $document = $reader->load($filename);
+ $extractedVariables = [];
+ foreach ($document->getSections() as $section) {
+ $concatenatedText = '';
+ foreach ($section->getElements() as $element) {
+ if ($element instanceof TextRun) {
+ foreach ($element->getElements() as $textElement) {
+ if ($textElement instanceof Text) {
+ $text = $textElement->getText();
+ $concatenatedText .= $text;
+ }
+ }
+ }
+ }
+ preg_match_all('/\$\{([^}]+)\}/', $concatenatedText, $matches);
+ if (!empty($matches[1])) {
+ foreach ($matches[1] as $match) {
+ $trimmedMatch = trim($match);
+ $extractedVariables[] = $trimmedMatch;
+ }
+ }
+ }
+
+ return $extractedVariables;
+ }
+
/**
* Check if it's a concrete class (not abstract nor interface).
*
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 1828de3b06..6be54b37a7 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -38,6 +38,8 @@
*/
class Html
{
+ private const RGB_REGEXP = '/^\s*rgb\s*[(]\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*[)]\s*$/';
+
protected static $listIndex = 0;
protected static $xpath;
@@ -143,7 +145,7 @@ protected static function parseInlineStyle($node, $styles = [])
break;
case 'bgcolor':
// tables, rows, cells e.g.
- $styles['bgColor'] = trim($val, '# ');
+ $styles['bgColor'] = self::convertRgb($val);
break;
case 'valign':
@@ -722,11 +724,11 @@ protected static function parseStyleDeclarations(array $selectors, array $styles
break;
case 'color':
- $styles['color'] = trim($value, '#');
+ $styles['color'] = self::convertRgb($value);
break;
case 'background-color':
- $styles['bgColor'] = trim($value, '#');
+ $styles['bgColor'] = self::convertRgb($value);
break;
case 'line-height':
@@ -1172,4 +1174,13 @@ protected static function parseHorizRule($node, $element): void
// - line - that is a shape, has different behaviour
// - repeated text, e.g. underline "_", because of unpredictable line wrapping
}
+
+ private static function convertRgb(string $rgb): string
+ {
+ if (preg_match(self::RGB_REGEXP, $rgb, $matches) === 1) {
+ return sprintf('%02X%02X%02X', $matches[1], $matches[2], $matches[3]);
+ }
+
+ return trim($rgb, '# ');
+ }
}
diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
index 5ff42e49b9..d6cf69fc6d 100644
--- a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
+++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php
@@ -34,6 +34,9 @@ class PasswordEncoder
const ALGORITHM_MAC = 'MAC';
const ALGORITHM_HMAC = 'HMAC';
+ private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;
+ private const HIGH_ORDER_BIT = (PHP_INT_SIZE > 4) ? 0x80000000 : PHP_INT_MIN;
+
/**
* Mapping between algorithm name and algorithm ID.
*
@@ -128,7 +131,7 @@ public static function hashPassword($password, $algorithmName = self::ALGORITHM_
// build low-order word and hig-order word and combine them
$combinedKey = self::buildCombinedKey($byteChars);
// build reversed hexadecimal string
- $hex = str_pad(strtoupper(dechex($combinedKey & 0xFFFFFFFF)), 8, '0', \STR_PAD_LEFT);
+ $hex = str_pad(strtoupper(dechex($combinedKey & self::ALL_ONE_BITS)), 8, '0', \STR_PAD_LEFT);
$reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1];
$generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8');
@@ -232,10 +235,10 @@ private static function buildCombinedKey($byteChars)
*/
private static function int32($value)
{
- $value = ($value & 0xFFFFFFFF);
+ $value = $value & self::ALL_ONE_BITS;
- if ($value & 0x80000000) {
- $value = -((~$value & 0xFFFFFFFF) + 1);
+ if ($value & self::HIGH_ORDER_BIT) {
+ $value = -((~$value & self::ALL_ONE_BITS) + 1);
}
return $value;
diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php
index 1ad901d480..8aee40c546 100644
--- a/src/PhpWord/TemplateProcessor.php
+++ b/src/PhpWord/TemplateProcessor.php
@@ -146,18 +146,6 @@ public function __destruct()
// Nothing to do here.
}
}
- // Temporary file
- if ($this->tempDocumentFilename && file_exists($this->tempDocumentFilename)) {
- unlink($this->tempDocumentFilename);
- }
- }
-
- public function __wakeup(): void
- {
- $this->tempDocumentFilename = '';
- $this->zipClass = null;
-
- throw new Exception('unserialize not permitted for this class');
}
/**
@@ -357,6 +345,15 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_
$replace = $xmlEscaper->escape($replace);
}
+ // convert carriage returns
+ if (is_array($replace)) {
+ foreach ($replace as &$item) {
+ $item = $this->replaceCarriageReturns($item);
+ }
+ } else {
+ $replace = $this->replaceCarriageReturns($replace);
+ }
+
$this->tempDocumentHeaders = $this->setValueForPart($search, $replace, $this->tempDocumentHeaders, $limit);
$this->tempDocumentMainPart = $this->setValueForPart($search, $replace, $this->tempDocumentMainPart, $limit);
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
@@ -1305,6 +1302,14 @@ protected function indexClonedVariables($count, $xmlBlock)
return $results;
}
+ /**
+ * Replace carriage returns with xml.
+ */
+ public function replaceCarriageReturns(string $string): string
+ {
+ return str_replace(["\r\n", "\r", "\n"], '', $string);
+ }
+
/**
* Replaces variables with values from array, array keys are the variable names.
*
@@ -1489,4 +1494,9 @@ public function setMacroChars(string $macroOpeningChars, string $macroClosingCha
self::$macroOpeningChars = $macroOpeningChars;
self::$macroClosingChars = $macroClosingChars;
}
+
+ public function getTempDocumentFilename(): string
+ {
+ return $this->tempDocumentFilename;
+ }
}
diff --git a/tests/PhpWordTests/Element/FormulaTest.php b/tests/PhpWordTests/Element/FormulaTest.php
index fef5c2221e..7e368e8995 100644
--- a/tests/PhpWordTests/Element/FormulaTest.php
+++ b/tests/PhpWordTests/Element/FormulaTest.php
@@ -30,7 +30,7 @@
class FormulaTest extends AbstractWebServerEmbeddedTest
{
/**
- * @covers \Formula::__construct
+ * @covers \PhpOffice\PhpWord\Element\Formula::__construct
*/
public function testConstruct(): void
{
@@ -40,8 +40,8 @@ public function testConstruct(): void
}
/**
- * @covers \Formula::getMath
- * @covers \Formula::setMath
+ * @covers \PhpOffice\PhpWord\Element\Formula::getMath
+ * @covers \PhpOffice\PhpWord\Element\Formula::setMath
*/
public function testMath(): void
{
diff --git a/tests/PhpWordTests/IOFactoryTest.php b/tests/PhpWordTests/IOFactoryTest.php
index ee8d7fe180..79f0fd0c76 100644
--- a/tests/PhpWordTests/IOFactoryTest.php
+++ b/tests/PhpWordTests/IOFactoryTest.php
@@ -116,4 +116,17 @@ public function testLoad(): void
IOFactory::load($file)
);
}
+
+ /**
+ * Test for extractVariables method.
+ */
+ public function testExtractVariables(): void
+ {
+ $file = __DIR__ . '/_files/templates/extract-variable.docx';
+ $extractedVariables = IOFactory::extractVariables($file, 'Word2007');
+
+ $expectedVariables = ['date', 'A1', 'B1'];
+
+ self::assertEquals($expectedVariables, $extractedVariables, 'Extracted variables do not match expected variables.');
+ }
}
diff --git a/tests/PhpWordTests/PhpWordTest.php b/tests/PhpWordTests/PhpWordTest.php
index 7b756e7082..33118a11e8 100644
--- a/tests/PhpWordTests/PhpWordTest.php
+++ b/tests/PhpWordTests/PhpWordTest.php
@@ -18,6 +18,7 @@
namespace PhpOffice\PhpWordTests;
use BadMethodCallException;
+use DateTimeImmutable;
use PhpOffice\PhpWord\Metadata\DocInfo;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
@@ -35,8 +36,14 @@ class PhpWordTest extends \PHPUnit\Framework\TestCase
*/
public function testConstruct(): void
{
- $phpWord = new PhpWord();
- self::assertEquals(new DocInfo(), $phpWord->getDocInfo());
+ do {
+ $dtStart = new DateTimeImmutable();
+ $startSecond = $dtStart->format('s');
+ $phpWord = new PhpWord();
+ $docInfo = new DocInfo();
+ $endSecond = (new DateTimeImmutable('now'))->format('s');
+ } while ($startSecond !== $endSecond);
+ self::assertEquals($docInfo, $phpWord->getDocInfo());
self::assertEquals(Settings::DEFAULT_FONT_NAME, $phpWord->getDefaultFontName());
self::assertEquals(Settings::DEFAULT_FONT_SIZE, $phpWord->getDefaultFontSize());
}
diff --git a/tests/PhpWordTests/TemplateProcessorTest.php b/tests/PhpWordTests/TemplateProcessorTest.php
index f2d2cfbf13..49e88d1b5b 100644
--- a/tests/PhpWordTests/TemplateProcessorTest.php
+++ b/tests/PhpWordTests/TemplateProcessorTest.php
@@ -21,7 +21,6 @@
use Exception;
use PhpOffice\PhpWord\Element\Text;
use PhpOffice\PhpWord\Element\TextRun;
-use PhpOffice\PhpWord\Exception\Exception as WordException;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
@@ -38,14 +37,36 @@
*/
final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
{
+ /** @var ?TemplateProcessor */
+ private $templateProcessor;
+
+ private function getTemplateProcessor(string $filename): TemplateProcessor
+ {
+ $this->templateProcessor = new TemplateProcessor($filename);
+
+ return $this->templateProcessor;
+ }
+
+ protected function tearDown(): void
+ {
+ if ($this->templateProcessor !== null) {
+ $filename = $this->templateProcessor->getTempDocumentFilename();
+ $this->templateProcessor = null;
+ if (file_exists($filename)) {
+ @unlink($filename);
+ }
+ }
+ }
+
/**
* Construct test.
*
* @covers ::__construct
+ * @covers ::__destruct
*/
public function testTheConstruct(): void
{
- $object = new TemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
+ $object = $this->getTemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
self::assertInstanceOf('PhpOffice\\PhpWord\\TemplateProcessor', $object);
self::assertEquals([], $object->getVariables());
}
@@ -106,7 +127,7 @@ public function xtestTemplateCanBeSavedInTemporaryLocation(string $templateFqfn,
public function testXslStyleSheetCanBeApplied(): void
{
$templateFqfn = __DIR__ . '/_files/templates/with_table_macros.docx';
- $templateProcessor = new TemplateProcessor($templateFqfn);
+ $templateProcessor = $this->getTemplateProcessor($templateFqfn);
$actualDocumentFqfn = $this->xtestTemplateCanBeSavedInTemporaryLocation($templateFqfn, $templateProcessor);
$expectedDocumentFqfn = __DIR__ . '/_files/documents/without_table_macros.docx';
@@ -150,7 +171,7 @@ public function testXslStyleSheetCanNotBeAppliedOnFailureOfSettingParameterValue
$this->expectExceptionMessage('Could not set values for the given XSL style sheet parameters.');
}
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
$xslDomDocument = new DOMDocument();
$xslDomDocument->load(__DIR__ . '/_files/xsl/passthrough.xsl');
@@ -171,7 +192,7 @@ public function testXslStyleSheetCanNotBeAppliedOnFailureOfLoadingXmlFromTemplat
{
$this->expectException(\PhpOffice\PhpWord\Exception\Exception::class);
$this->expectExceptionMessage('Could not load the given XML document.');
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/corrupted_main_document_part.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/corrupted_main_document_part.docx');
$xslDomDocument = new DOMDocument();
$xslDomDocument->load(__DIR__ . '/_files/xsl/passthrough.xsl');
@@ -190,7 +211,7 @@ public function testXslStyleSheetCanNotBeAppliedOnFailureOfLoadingXmlFromTemplat
*/
public function testDeleteRow(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
self::assertEquals(
['deleteMe', 'deleteMeToo'],
@@ -216,7 +237,7 @@ public function testDeleteRow(): void
*/
public function testCloneRow(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/clone-merge.docx');
self::assertEquals(
['tableHeader', 'userId', 'userName', 'userLocation'],
@@ -240,7 +261,7 @@ public function testCloneRow(): void
*/
public function testCloneRowWithCustomMacro(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
$templateProcessor->setMacroOpeningChars('{#');
$templateProcessor->setMacroClosingChars('#}');
@@ -397,7 +418,7 @@ public function testCloneRowAndSetValuesWithCustomMacro(): void
*/
public function testMacrosCanBeReplacedInHeaderAndFooter(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
self::assertEquals(['documentContent', 'headerValue:100:100', 'footerValue'], $templateProcessor->getVariables());
@@ -418,7 +439,7 @@ public function testMacrosCanBeReplacedInHeaderAndFooter(): void
*/
public function testCustomMacrosCanBeReplacedInHeaderAndFooter(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer-with-custom-macro.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/header-footer-with-custom-macro.docx');
$templateProcessor->setMacroOpeningChars('{{');
$templateProcessor->setMacroClosingChars('}}');
@@ -440,7 +461,7 @@ public function testCustomMacrosCanBeReplacedInHeaderAndFooter(): void
*/
public function testSetValue(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/clone-merge.docx');
Settings::setOutputEscapingEnabled(true);
$helloworld = "hello\nworld";
$templateProcessor->setValue('userName', $helloworld);
@@ -455,7 +476,7 @@ public function testSetValue(): void
*/
public function testSetValueWithCustomMacro(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/clone-merge-with-custom-macro.docx');
$templateProcessor->setMacroChars('{#', '#}');
Settings::setOutputEscapingEnabled(true);
$helloworld = "hello\nworld";
@@ -591,6 +612,24 @@ public function testSetValues(): void
self::assertStringContainsString('Hello John Doe', $templateProcessor->getMainPart());
}
+ /**
+ * @covers ::setValues
+ */
+ public function testSetValuesMultiLine(): void
+ {
+ $mainPart = '
+
+
+ Address: ${address}
+
+ ';
+
+ $templateProcessor = new TestableTemplateProcesor($mainPart);
+ $templateProcessor->setValues(['address' => "Peter Pan\nNeverland"]);
+
+ self::assertStringContainsString('Address: Peter PanNeverland', $templateProcessor->getMainPart());
+ }
+
/**
* @covers ::setValues
*/
@@ -768,7 +807,7 @@ public function testSetCheckboxWithCustomMacro(): void
*/
public function testSetImageValue(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
$imagePath = __DIR__ . '/_files/images/earth.jpg';
$variablesReplace = [
@@ -848,7 +887,7 @@ public function testSetImageValue(): void
*/
public function testCloneDeleteBlock(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/clone-delete-block.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/clone-delete-block.docx');
self::assertEquals(
['DELETEME', '/DELETEME', 'CLONEME', 'blockVariable', '/CLONEME'],
@@ -888,7 +927,7 @@ public function testGetVariableCountCountsHowManyTimesEachPlaceholderIsPresent()
$templatePath = 'test.docx';
$objWriter->save($templatePath);
- $templateProcessor = new TemplateProcessor($templatePath);
+ $templateProcessor = $this->getTemplateProcessor($templatePath);
$variableCount = $templateProcessor->getVariableCount();
unlink($templatePath);
@@ -925,7 +964,7 @@ public function testGetVariableCountCountsHowManyTimesEachPlaceholderIsPresentWi
$templatePath = 'test.docx';
$objWriter->save($templatePath);
- $templateProcessor = new TemplateProcessor($templatePath);
+ $templateProcessor = $this->getTemplateProcessor($templatePath);
$templateProcessor->setMacroChars('{{', '}}');
$variableCount = $templateProcessor->getVariableCount();
unlink($templatePath);
@@ -963,7 +1002,7 @@ public function testCloneBlockCanCloneABlockTwice(): void
$objWriter->save($templatePath);
// replace placeholders and save the file
- $templateProcessor = new TemplateProcessor($templatePath);
+ $templateProcessor = $this->getTemplateProcessor($templatePath);
$templateProcessor->setValue('title', 'Some title');
$templateProcessor->cloneBlock('subreport', 2);
$templateProcessor->setValue('subreport.id', '123', 1);
@@ -1016,7 +1055,7 @@ public function testCloneBlockCanCloneABlockTwiceWithCustomMacro(): void
$objWriter->save($templatePath);
// replace placeholders and save the file
- $templateProcessor = new TemplateProcessor($templatePath);
+ $templateProcessor = $this->getTemplateProcessor($templatePath);
$templateProcessor->setMacroChars('{{', '}}');
$templateProcessor->setValue('title', 'Some title');
$templateProcessor->cloneBlock('subreport', 2);
@@ -1305,7 +1344,7 @@ public function testFixBrokenMacrosWithCustomMacro(): void
*/
public function testMainPartNameDetection(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/document22-xml.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/document22-xml.docx');
$variables = ['test'];
@@ -1317,7 +1356,7 @@ public function testMainPartNameDetection(): void
*/
public function testMainPartNameDetectionWithCustomMacro(): void
{
- $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/document22-with-custom-macro-xml.docx');
+ $templateProcessor = $this->getTemplateProcessor(__DIR__ . '/_files/templates/document22-with-custom-macro-xml.docx');
$templateProcessor->setMacroOpeningChars('{#');
$templateProcessor->setMacroClosingChars('#}');
$variables = ['test'];
@@ -1577,18 +1616,6 @@ public function testShouldMakeFieldsUpdateOnOpen(): void
self::assertStringContainsString('', $templateProcessor->getSettingsPart());
}
- /**
- * Should not allow unserialize to avoid malware.
- */
- public function testUnserialize(): void
- {
- $this->expectException(WordException::class);
- $this->expectExceptionMessage('unserialize not permitted');
- $object = new TemplateProcessor(__DIR__ . '/_files/templates/blank.docx');
- $serialized = serialize($object);
- $object2 = unserialize($serialized);
- }
-
public function testShouldMakeFieldsUpdateOnOpenWithCustomMacro(): void
{
$settingsPart = '
diff --git a/tests/PhpWordTests/Writer/Word2007/Style/FontTest.php b/tests/PhpWordTests/Writer/Word2007/Style/FontTest.php
index 2c10b5ff2f..a8214ec35b 100644
--- a/tests/PhpWordTests/Writer/Word2007/Style/FontTest.php
+++ b/tests/PhpWordTests/Writer/Word2007/Style/FontTest.php
@@ -17,6 +17,8 @@
namespace PhpOffice\PhpWordTests\Writer\Word2007\Style;
+use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Shared\Html;
use PhpOffice\PhpWordTests\TestHelperDOCX;
/**
@@ -155,4 +157,44 @@ public function testPosition(): void
self::assertTrue($doc->elementExists($path));
self::assertEquals(-20, $doc->getElementAttribute($path, 'w:val'));
}
+
+ public static function testRgb(): void
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection(['pageNumberingStart' => 1]);
+ $html = implode(
+ "\n",
+ [
+ '',
+ '',
+ '',
+ 'This one is in color. | ',
+ 'This one too. | ',
+ '
',
+ '',
+ '
',
+ ]
+ );
+
+ Html::addHtml($section, $html, false, false);
+ $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
+
+ $element = '/w:document/w:body/w:tbl/w:tr/w:tc/w:p/w:r';
+ $txtelem = $element . '/w:t';
+ $styelem = $element . '/w:rPr';
+ self::assertTrue($doc->elementExists($txtelem));
+ self::assertSame('This one is in color.', $doc->getElement($txtelem)->textContent);
+ self::assertTrue($doc->elementExists($styelem));
+ self::assertTrue($doc->elementExists($styelem . '/w:color'));
+ self::assertSame('A7D9C1', $doc->getElementAttribute($styelem . '/w:color', 'w:val'));
+
+ $element = '/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:p/w:r';
+ $txtelem = $element . '/w:t';
+ $styelem = $element . '/w:rPr';
+ self::assertTrue($doc->elementExists($txtelem));
+ self::assertSame('This one too.', $doc->getElement($txtelem)->textContent);
+ self::assertTrue($doc->elementExists($styelem));
+ self::assertTrue($doc->elementExists($styelem . '/w:color'));
+ self::assertSame('A7D9C1', $doc->getElementAttribute($styelem . '/w:color', 'w:val'));
+ }
}
diff --git a/tests/PhpWordTests/_files/templates/extract-variable.docx b/tests/PhpWordTests/_files/templates/extract-variable.docx
new file mode 100644
index 0000000000..f95ec61862
Binary files /dev/null and b/tests/PhpWordTests/_files/templates/extract-variable.docx differ