最近,由于公司在某些程度上要生成word文档. 在网上于是就找了PHPOffice团队出品的PHPword. 说实话,在用的过程中很不错. 但是有一些特殊的属性就不支持. 例如, 允许在西文单词中换行这个属性就没有. 下面就记录一下我整个的过程.
源码
我们通过源代码查看, 在最终生成word的时候, 是使用的writer类的save方法. 如下
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
// 开始处理word.
$word = new PhpWord();
$writer = IOFactory::createWriter($word, 'Word2007');
$full_path = \Yii::$app->getRuntimePath() . DIRECTORY_SEPARATOR . '域名.docx';
$writer->save($full_path);
我们找到writer类的save方法.
// vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007.php
// 137行左右
foreach ($this->parts as $partName => $fileName) {
if ($fileName != '') {
// 这段话用了zip的方式.
$zip->addFromString($fileName, $this->getWriterPart($partName)->write());
}
}
在137行左右使用了addFromString方法. 这个是用zip扩展来进行生成的.zip扩展是用来创建压缩文件 . 难道word是一个zip文件? so 那我们来试试. 将导出的文件改成.zip为后缀的压缩文件. 如下
不难发现. 我们成功了. 在zip里面我们果断看到了一堆xml. 经过查找. 在zip文件中 word/document.xml这个里面会含有我们的文字信息. 和一些排版样式. 剩下的我们就需要做对比了. 将一个有西文单词换行的和一个没有西文单词换行的来进行对比. 如下图所示. 仅仅是多了这几列.
左侧是勾选了允许在西文单词中换行这个属性, 右侧则是没有. 剩下就是根据这个规则生成就可以了.
修改源码
经过阅读网址, 发现我们只需要实现wordwrap就可以了. 开干. 我们先看看其他属性的实现. 例如keepNext. 他的调用方式如下
$word = new PhpWord();
$section = $word->addSection();
$element = $section->addText('There are moments in life when you miss someone so much that you just want to pick them supercalifragilisticexpiadocious your dreams and hug them for real! Dream what you want to dream;go where you want to go;be what you want to be,because you have only one life and one chance to do all the things you want to do.');
$paragraph = new Paragraph();
// 增加段落标识.
$paragraph->setKeepNext(true);
$element->setParagraphStyle($paragraph);
$writer = IOFactory::createWriter($word, 'Word2007');
$writer->save(\Yii::$app->getRuntimePath() . '/1.docx');
那我们就在Paragraph中增加setWordWrap方法. 同时我们还发现他有hasKeepNext. 也是需要我们增加的.
// 默认是不允许的
private $wordWrap = true;
// 是由含有允许在西文单词中换行属性
public function hasWordWrap()
{
return $this->wordWrap;
}
// 设置允许在西文单词中换行
public function setWordWrap($value = true)
{
$this->wordWrap = $this->setBoolVal($value, $this->wordWrap);
return $this;
}
public function getStyleValues()
{
$styles = array(
'name' => $this->getStyleName(),
'basedOn' => $this->getBasedOn(),
'next' => $this->getNext(),
'alignment' => $this->getAlignment(),
'indentation' => $this->getIndentation(),
'spacing' => $this->getSpace(),
'pagination' => array(
'widowControl' => $this->hasWidowControl(),
'keepNext' => $this->isKeepNext(),
'keepLines' => $this->isKeepLines(),
'pageBreak' => $this->hasPageBreakBefore(),
// 新增.
'wordWrap' => $this->hasWordWrap(),
),
'numbering' => array(
'style' => $this->getNumStyle(),
'level' => $this->getNumLevel(),
),
'tabs' => $this->getTabs(),
'shading' => $this->getShading(),
'contextualSpacing' => $this->hasContextualSpacing(),
'bidi' => $this->isBidi(),
'textAlignment' => $this->getTextAlignment(),
'suppressAutoHyphens' => $this->hasSuppressAutoHyphens(),
);
return $styles;
}
同时. 在输出的时候 我们需要增加这个element. 所以我们在phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Paragraph.php, 96行增加如下代码
$xmlWriter->writeElementIf($styles['pagination']['wordWrap'] !== true, 'w:wordWrap', 'w:val', '0');
最后
修改测试代码
$word = new PhpWord();
$section = $word->addSection();
$element = $section->addText('There are moments in life when you miss someone so much that you just want to pick them supercalifragilisticexpiadocious your dreams and hug them for real! Dream what you want to dream;go where you want to go;be what you want to be,because you have only one life and one chance to do all the things you want to do.');
$paragraph = new Paragraph();
// 增加段落标识.
$paragraph->setWordWrap(false);
$element->setParagraphStyle($paragraph);
$writer = IOFactory::createWriter($word, 'Word2007');
$writer->save(\Yii::$app->getRuntimePath() . '/1.docx');
测试一下, 完美解决. 下面是我的一些参考文档
http://officeopenxml.com/WPparagraphProperties.php
https://docs.microsoft.com/zh-cn/office/vba/api/word.paragraph