Skip to content

PhpWord fails to escape & in generated <w:t> content #2884

Description

@paulw3nerd

Describe the bug and add attachments

This is a PhpWord bug: Html::addHtml() correctly decodes & → & from HTML, but when PhpWord writes the OOXML, it fails to re-escape & back to & inside <w:t> tags, so a broken file is created that Word can't open.

I fixed with the below:
//fix for phpword bug - PhpWord doesn't escape & in <w:t> content, so post-fix the generated XML
$zipFix = new ZipArchive();
if ($zipFix->open($tempFile) === true) {
$docXml = $zipFix->getFromName('word/document.xml');
// Fix bare & that PhpWord failed to escape (only unescaped ones, not & etc.)
$docXml = preg_replace('/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[0-9a-fA-F]+;)/i', '&', $docXml);
$zipFix->addFromString('word/document.xml', $docXml);
$zipFix->close();
}

Expected behavior

Generate a file that can be opened by MS Word.

Steps to reproduce

					$phpWord = new PhpWord();
					$phpWord->setDefaultFontName('Times New Roman');
					$phpWord->setDefaultFontSize(10);
					$margins = [
						'marginLeft'   => 720,
						'marginRight'  => 720,
						'marginTop'    => 720,
						'marginBottom' => 720,
					];
					$section = $phpWord->addSection($margins);
					$footer = $section->addFooter();
					$footer->addText('TEST', array('italic' => true));

$htmlTestStr = '

'; $htmlTestStr .= '

Prepared By / Record & Return To:

'; $htmlTestStr .= ' '; $htmlTestStr = str_ireplace('
', '
', $htmlTestStr); // Escape bare ampersands that aren't already part of an HTML entity $htmlTestStr = preg_replace('/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[0-9a-fA-F]+;)/i', '&', $htmlTestStr); //element,html,fullHTML,preserveWhiteSpace Html::addHtml($section, $htmlTestStr, true, false); $tempFile = tempnam(sys_get_temp_dir(), 'phpword'); $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); $objWriter->save($tempFile);

//fix for phpword bug - PhpWord doesn't escape & in <w:t> content, so post-fix the generated XML
$zipFix = new ZipArchive();
if ($zipFix->open($tempFile) === true) {
$docXml = $zipFix->getFromName('word/document.xml');
// Fix bare & that PhpWord failed to escape (only unescaped ones, not & etc.)
$docXml = preg_replace('/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[0-9a-fA-F]+;)/i', '&', $docXml);
$zipFix->addFromString('word/document.xml', $docXml);
$zipFix->close();
}

					//Set download headers
					header('Content-Description: File Transfer');
					header('Content-Type: application/octet-stream');
					header('Content-Disposition: attachment; filename="test.docx"');
					header('Content-Transfer-Encoding: binary');
					header('Expires: 0');
					header('Cache-Control: must-revalidate');
					header('Pragma: public');
					header('Content-Length: ' . filesize($tempFile));

					// Output file and delete the temp file
					readfile($tempFile);
					unlink($tempFile);
					exit;

PHPWord version(s) where the bug happened

1.4

PHP version(s) where the bug happened

8.3

Priority

  • I want to crowdfund the bug fix (with @algora-io) and fund a community developer.
  • I want to pay the bug fix and fund a maintainer for that. (Contact @Progi1984)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions