"zendframework/zend-stdlib": "2.4.",//替换图片用 "zendframework/zend-validator": "2.4.", "phpoffice/phpword": "dev-master"//phpword
<?php
namespace App\Http\Controllers\Api\Test;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TestController extends Controller
{
public function test(){
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('my_template.docx');
$templateProcessor->setValue('name', '(1S,4R)-4,7,7-Trimethyl-3-oxo-2-oxabicyclo[2.2.1]heptane-1-carbonyl chloride');
$arrImagenes=array('src' => '1.png');
$templateProcessor->setImg('img', $arrImagenes);
$templateProcessor->setValue('bnumber', '2018-004-050');
$templateProcessor->setValue('cas', '39637-74-6 ');
$templateProcessor->setValue('img', '39637-74-6 ');
$templateProcessor->setValue('mdate', '2018-04-05');
$templateProcessor->setValue('mf', 'C10H13ClO3');
$templateProcessor->setValue('mw', '216.6614');
$templateProcessor->setValue('quantity', '10kg');
$templateProcessor->setValue('appearance', 'White crystal');
$templateProcessor->setValue('appearanceResults', 'Complies');
$templateProcessor->setValue('hnmr', 'Confirms to structure');
$templateProcessor->setValue('purity', '≥98%');
$templateProcessor->setValue('puirtyResults', 'Complies');
//生成的文档为Word2007
$templateProcessor->saveAs('test.docx');
}
}
如果插入图片需要修改CLASS TemplateProcessor;
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2010-2016 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Escaper\RegExp;
use PhpOffice\PhpWord\Escaper\Xml;
use PhpOffice\PhpWord\Exception\CopyFileException;
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Shared\ZipArchive;
use Zend\Stdlib\StringUtils;
class TemplateProcessor
{
const MAXIMUM_REPLACEMENTS_DEFAULT = -1;
/**
* ZipArchive object.
*
* @var mixed
*/
protected $zipClass;
/**
* @var string Temporary document filename (with path).
*/
protected $tempDocumentFilename;
/**
* Content of main document part (in XML format) of the temporary document.
*
* @var string
*/
protected $tempDocumentMainPart;
/**
* Content of headers (in XML format) of the temporary document.
*
* @var string[]
*/
protected $tempDocumentHeaders = array();
/**
* Content of footers (in XML format) of the temporary document.
*
* @var string[]
*/
protected $tempDocumentFooters = array();
protected $_rels;
protected $_types;
/**
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception.
*
* @param string $documentTemplate The fully qualified template filename.
*
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
*/
public function __construct($documentTemplate)
{
$this->_countRels=100;
// Temporary document filename initialization
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
if (false === $this->tempDocumentFilename) {
throw new CreateTemporaryFileException();
}
// Template file cloning
if (false === copy($documentTemplate, $this->tempDocumentFilename)) {
throw new CopyFileException($documentTemplate, $this->tempDocumentFilename);
}
// Temporary document content extraction
$this->zipClass = new ZipArchive();
$this->zipClass->open($this->tempDocumentFilename);
$index = 1;
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
$this->tempDocumentHeaders[$index] = $this->fixBrokenMacros(
$this->zipClass->getFromName($this->getHeaderName($index))
);
$index++;
}
$index = 1;
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
$this->tempDocumentFooters[$index] = $this->fixBrokenMacros(
$this->zipClass->getFromName($this->getFooterName($index))
);
$index++;
}
$this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName($this->getMainPartName()));
}
/**
* @param string $xml
* @param \XSLTProcessor $xsltProcessor
*
* @return string
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
protected function transformSingleXml($xml, $xsltProcessor)
{
$domDocument = new \DOMDocument();
if (false === $domDocument->loadXML($xml)) {
throw new Exception('Could not load the given XML document.');
}
$transformedXml = $xsltProcessor->transformToXml($domDocument);
if (false === $transformedXml) {
throw new Exception('Could not transform the given XML document.');
}
return $transformedXml;
}
/**
* @param mixed $xml
* @param \XSLTProcessor $xsltProcessor
*
* @return mixed
*/
protected function transformXml($xml, $xsltProcessor)
{
if (is_array($xml)) {
foreach ($xml as &$item) {
$item = $this->transformSingleXml($item, $xsltProcessor);
}
} else {
$xml = $this->transformSingleXml($xml, $xsltProcessor);
}
return $xml;
}
/**
* Applies XSL style sheet to template's parts.
*
* Note: since the method doesn't make any guess on logic of the provided XSL style sheet,
* make sure that output is correctly escaped. Otherwise you may get broken document.
*
* @param \DOMDocument $xslDomDocument
* @param array $xslOptions
* @param string $xslOptionsUri
*
* @return void
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function applyXslStyleSheet($xslDomDocument, $xslOptions = array(), $xslOptionsUri = '')
{
$xsltProcessor = new \XSLTProcessor();
$xsltProcessor->importStylesheet($xslDomDocument);
if (false === $xsltProcessor->setParameter($xslOptionsUri, $xslOptions)) {
throw new Exception('Could not set values for the given XSL style sheet parameters.');
}
$this->tempDocumentHeaders = $this->transformXml($this->tempDocumentHeaders, $xsltProcessor);
$this->tempDocumentMainPart = $this->transformXml($this->tempDocumentMainPart, $xsltProcessor);
$this->tempDocumentFooters = $this->transformXml($this->tempDocumentFooters, $xsltProcessor);
}
/**
* @param string $macro
*
* @return string
*/
protected static function ensureMacroCompleted($macro)
{
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
$macro = '${' . $macro . '}';
}
return $macro;
}
/**
* @param string $subject
*
* @return string
*/
protected static function ensureUtf8Encoded($subject)
{
if (!StringUtils::isValidUtf8($subject)) {
$subject = utf8_encode($subject);
}
return $subject;
}
/**
* @param mixed $search
* @param mixed $replace
* @param integer $limit
*
* @return void
*/
public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
{
if (is_array($search)) {
foreach ($search as &$item) {
$item = self::ensureMacroCompleted($item);
}
} else {
$search = self::ensureMacroCompleted($search);
}
if (is_array($replace)) {
foreach ($replace as &$item) {
$item = self::ensureUtf8Encoded($item);
}
} else {
$replace = self::ensureUtf8Encoded($replace);
}
if (Settings::isOutputEscapingEnabled()) {
$xmlEscaper = new Xml();
$replace = $xmlEscaper->escape($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);
}
/**
* Returns array of all variables in template.
*
* @return string[]
*/
public function getVariables()
{
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
foreach ($this->tempDocumentHeaders as $headerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
}
foreach ($this->tempDocumentFooters as $footerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
}
return array_unique($variables);
}
/**
* Clone a table row in a template document.
*
* @param string $search
* @param integer $numberOfClones
*
* @return void
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function cloneRow($search, $numberOfClones)
{
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
$search = '${' . $search . '}';
}
$tagPos = strpos($this->tempDocumentMainPart, $search);
if (!$tagPos) {
throw new Exception("Can not clone row, template variable not found or variable contains markup.");
}
$rowStart = $this->findRowStart($tagPos);
$rowEnd = $this->findRowEnd($tagPos);
$xmlRow = $this->getSlice($rowStart, $rowEnd);
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
// $extraRowStart = $rowEnd;
$extraRowEnd = $rowEnd;
while (true) {
$extraRowStart = $this->findRowStart($extraRowEnd + 1);
$extraRowEnd = $this->findRowEnd($extraRowEnd + 1);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
break;
}
// This row was a spanned row, update $rowEnd and search for the next row.
$rowEnd = $extraRowEnd;
}
$xmlRow = $this->getSlice($rowStart, $rowEnd);
}
$result = $this->getSlice(0, $rowStart);
for ($i = 1; $i <= $numberOfClones; $i++) {
$result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
}
$result .= $this->getSlice($rowEnd);
$this->tempDocumentMainPart = $result;
}
/**
* Clone a block.
*
* @param string $blockname
* @param integer $clones
* @param boolean $replace
*
* @return string|null
*/
public function cloneBlock($blockname, $clones = 1, $replace = true)
{
$xmlBlock = null;
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart,
$matches
);
if (isset($matches[3])) {
$xmlBlock = $matches[3];
$cloned = array();
for ($i = 1; $i <= $clones; $i++) {
$cloned[] = $xmlBlock;
}
if ($replace) {
$this->tempDocumentMainPart = str_replace(
$matches[2] . $matches[3] . $matches[4],
implode('', $cloned),
$this->tempDocumentMainPart
);
}
}
return $xmlBlock;
}
/**
* Replace a block.
*
* @param string $blockname
* @param string $replacement
*
* @return void
*/
public function replaceBlock($blockname, $replacement)
{
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart,
$matches
);
if (isset($matches[3])) {
$this->tempDocumentMainPart = str_replace(
$matches[2] . $matches[3] . $matches[4],
$replacement,
$this->tempDocumentMainPart
);
}
}
/**
* Delete a block of text.
*
* @param string $blockname
*
* @return void
*/
public function deleteBlock($blockname)
{
$this->replaceBlock($blockname, '');
}
/**
* Saves the result document.
*
* @return string
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function save()
{
foreach ($this->tempDocumentHeaders as $index => $xml) {
$this->zipClass->addFromString($this->getHeaderName($index), $xml);
}
$this->zipClass->addFromString($this->getMainPartName(), $this->tempDocumentMainPart);
if($this->_rels!="")
{
$this->zipClass->addFromString('word/_rels/document.xml.rels', $this->_rels);
}
if($this->_types!="")
{
$this->zipClass->addFromString('[Content_Types].xml', $this->_types);
}
foreach ($this->tempDocumentFooters as $index => $xml) {
$this->zipClass->addFromString($this->getFooterName($index), $xml);
}
// Close zip file
if (false === $this->zipClass->close()) {
throw new Exception('Could not close zip file.');
}
return $this->tempDocumentFilename;
}
/**
* Saves the result document to the user defined file.
*
* @since 0.8.0
*
* @param string $fileName
*
* @return void
*/
public function saveAs($fileName)
{
$tempFileName = $this->save();
if (file_exists($fileName)) {
unlink($fileName);
}
/*
* Note: we do not use `rename` function here, because it looses file ownership data on Windows platform.
* As a result, user cannot open the file directly getting "Access denied" message.
*
* @see https://github.com/PHPOffice/PHPWord/issues/532
*/
copy($tempFileName, $fileName);
unlink($tempFileName);
}
/**
* Finds parts of broken macros and sticks them together.
* Macros, while being edited, could be implicitly broken by some of the word processors.
*
* @param string $documentPart The document part in XML representation.
*
* @return string
*/
protected function fixBrokenMacros($documentPart)
{
$fixedDocumentPart = $documentPart;
$fixedDocumentPart = preg_replace_callback(
'|\$[^{]*\{[^}]*\}|U',
function ($match) {
return strip_tags($match[0]);
},
$fixedDocumentPart
);
return $fixedDocumentPart;
}
/**
* Find and replace macros in the given XML section.
*
* @param mixed $search
* @param mixed $replace
* @param string $documentPartXML
* @param integer $limit
*
* @return string
*/
protected function setValueForPart($search, $replace, $documentPartXML, $limit)
{
// Note: we can't use the same function for both cases here, because of performance considerations.
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
return str_replace($search, $replace, $documentPartXML);
} else {
$regExpEscaper = new RegExp();
return preg_replace($regExpEscaper->escape($search), $replace, $documentPartXML, $limit);
}
}
/**
* Find all variables in $documentPartXML.
*
* @param string $documentPartXML
*
* @return string[]
*/
protected function getVariablesForPart($documentPartXML)
{
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
return $matches[1];
}
/**
* Get the name of the header file for $index.
*
* @param integer $index
*
* @return string
*/
protected function getHeaderName($index)
{
return sprintf('word/header%d.xml', $index);
}
/**
* @return string
*/
protected function getMainPartName()
{
return 'word/document.xml';
}
/**
* Get the name of the footer file for $index.
*
* @param integer $index
*
* @return string
*/
protected function getFooterName($index)
{
return sprintf('word/footer%d.xml', $index);
}
/**
* Find the start position of the nearest table row before $offset.
*
* @param integer $offset
*
* @return integer
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
protected function findRowStart($offset)
{
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr ', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
if (!$rowStart) {
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr>', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
}
if (!$rowStart) {
throw new Exception('Can not find the start position of the row to clone.');
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*
* @param integer $offset
*
* @return integer
*/
protected function findRowEnd($offset)
{
return strpos($this->tempDocumentMainPart, '</w:tr>', $offset) + 7;
}
/**
* Get a slice of a string.
*
* @param integer $startPosition
* @param integer $endPosition
*
* @return string
*/
protected function getSlice($startPosition, $endPosition = 0)
{
if (!$endPosition) {
$endPosition = strlen($this->tempDocumentMainPart);
}
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
}
public function setImg( $strKey, $img){
$strKey = '${'.$strKey.'}';
$relationTmpl = '<Relationship Id="RID" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/IMG"/>';
$imgTmpl = '<w:pict><v:shape type="#_x0000_t75" style="width:WIDpx;height:HEIpx"><v:imagedata r:id="RID" o:title=""/></v:shape></w:pict>';
$toAdd = $toAddImg = $toAddType = '';
$aSearch = array('RID', 'IMG');
$aSearchType = array('IMG', 'EXT');
$countrels=$this->_countRels++;
//I'm work for jpg files, if you are working with other images types -> Write conditions here
$imgExt = 'jpg';
$imgName = 'img' . $countrels . '.' . $imgExt;
$this->zipClass->deleteName('word/media/' . $imgName);
$this->zipClass->addFile($img['src'], 'word/media/' . $imgName);
$typeTmpl = '<Override PartName="/word/media/'.$imgName.'" ContentType="image/EXT"/>';
$rid = 'rId' . $countrels;
$countrels++;
list($w,$h) = getimagesize($img['src']);
if(isset($img['swh'])) //Image proportionally larger side
{
if($w<=$h)
{
$ht=(int)$img['swh'];
$ot=$w/$h;
$wh=(int)$img['swh']*$ot;
$wh=round($wh);
}
if($w>=$h)
{
$wh=(int)$img['swh'];
$ot=$h/$w;
$ht=(int)$img['swh']*$ot;
$ht=round($ht);
}
$w=$wh;
$h=$ht;
}
if(isset($img['size']))
{
$w = $img['size'][0];
$h = $img['size'][1];
}
$toAddImg .= str_replace(array('RID', 'WID', 'HEI'), array($rid, $w, $h), $imgTmpl) ;
if(isset($img['dataImg']))
{
$toAddImg.='<w:br/><w:t>'.$this->limpiarString($img['dataImg']).'</w:t><w:br/>';
}
$aReplace = array($imgName, $imgExt);
$toAddType .= str_replace($aSearchType, $aReplace, $typeTmpl) ;
$aReplace = array($rid, $imgName);
$toAdd .= str_replace($aSearch, $aReplace, $relationTmpl);
$this->tempDocumentMainPart=str_replace('<w:t>' . $strKey . '</w:t>', $toAddImg, $this->tempDocumentMainPart);
//print $this->tempDocumentMainPart;
if($this->_rels=="")
{
$this->_rels=$this->zipClass->getFromName('word/_rels/document.xml.rels');
$this->_types=$this->zipClass->getFromName('[Content_Types].xml');
}
$this->_types = str_replace('</Types>', $toAddType, $this->_types) . '</Types>';
$this->_rels = str_replace('</Relationships>', $toAdd, $this->_rels) . '</Relationships>';
}
function limpiarString($str) {
return str_replace(
array('&', '<', '>', "\n"),
array('&', '<', '>', "\n" . '<w:br/>'),
$str
);
}
}
本文为wjw原创文章,转载无需和我联系,但请注明来自wjw博客http://wjiawen.cn
最新评论