Last active
March 15, 2022 06:28
-
-
Save jeffsrepoaccount/1c1ded62c07b08d73c72de38b6f4ee42 to your computer and use it in GitHub Desktop.
PHPOffice/PHPWord - Injecting AltChunks in word document using placeholders in template file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php namespace My\Namespace; | |
use DOMDocument; | |
use Parsedown; | |
use PhpOffice\PhpWord\TemplateProcessor as PhpWordTemplateProcessor; | |
class TemplateProcessor extends PhpWordTemplateProcessor | |
{ | |
/** | |
* Injects a markdown snippet as HTML into the word document. | |
* | |
* @param string $search Template placeholder to replace | |
* @param string $markdown | |
* @return string | |
*/ | |
public function replaceMarkdown($search, $markdown) | |
{ | |
$parsedown = new Parsedown; | |
$contents = '<html><head /><body>' . | |
$parsedown->text($markdown) | |
. '</body></html>' | |
; | |
$this->registerAltChunk( | |
$search, | |
'word/' . $search . '.dat', | |
$contents | |
); | |
} | |
/** | |
* Registers an altChunk in the document. | |
* | |
* @param string $search Template placeholder | |
* @param string $target Base filename target added to archive | |
* @param string $contents HTML content to add to document | |
* @param string $type OOXML AltChunk Content Type | |
*/ | |
private function registerAltChunk($search, $target, $contents, $type = 'text/html') | |
{ | |
$id = 'alt_' . $search; | |
$this->zipClass->addFromString($target, $contents); | |
parent::setValue($search, '<w:altChunk r:id="' . $id . '" />'); | |
$this | |
->registerContentTypeOverride($target, $type) | |
->registerRelationship( | |
$id, | |
$target, | |
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk' | |
) | |
; | |
// Close/Reopen ziparchive so subsequent reads will see these writes | |
// Probably a better way to do this. | |
$this->zipClass->close(); | |
$this->zipClass->open($this->tempDocumentFilename); | |
} | |
/** | |
* Registers a content type override in the document in [Content_Types].xml | |
* | |
* @param string $target | |
* @param string $type | |
*/ | |
private function registerContentTypeOverride($target, $type) | |
{ | |
$contentTypes = new DOMDocument; | |
$contentTypes->loadXML( $this->zipClass->getFromName('[Content_Types].xml') ); | |
$node = $contentTypes->createElement('Override'); | |
$node->setAttribute('PartName', '/' . $target); | |
$node->setAttribute('ContentType', $type); | |
$root = $contentTypes->getElementsByTagName('Types')->item(0); | |
$root->appendChild($node); | |
$this->zipClass->addFromString('[Content_Types].xml', $contentTypes->saveXML()); | |
return $this; | |
} | |
/** | |
* Registers a relationship to the main document part. | |
* | |
* @param string $id The main document ID attribute | |
* @param string $target Filename target inside of the archive | |
* @param string $type Type to register the relationship as | |
*/ | |
private function registerRelationship($id, $target, $type) | |
{ | |
$relationships = new DOMDocument; | |
$relationships->loadXML($this->zipClass->getFromName('word/_rels/document.xml.rels')); | |
$node = $relationships->createElement('Relationship'); | |
$node->setAttribute('Id', $id); | |
$node->setAttribute('Type', $type); | |
$node->setAttribute('Target', '/' . $target); | |
$root = $relationships->getElementsByTagName('Relationships')->item(0); | |
$root->appendChild($node); | |
$this->zipClass->addFromString('word/_rels/document.xml.rels', $relationships->saveXML()); | |
return $this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage:
Note that the
<w:AltChunk />
element follows the same placement rules as<w:p>
elements. Just placing the placeholder string${replaceMe}
in the document may not be sufficient, you may need to modify the main document part (document.xml) inside of the DOCX archive of your template so that you guarantee it is not inside of a text run or similar elemnts (e.g.<w:r>
or<w:Pr>
). It most likely needs to be placed as a sibling of the nearest paragraph element.