美文网首页
如何实现图文word生成

如何实现图文word生成

作者: 豆腐匠 | 来源:发表于2020-02-09 11:46 被阅读0次

    前言

    我们有一个产品,就是分析某领域的专业分析报告,直接在页面上呈现出来。早期实现了一版html版本,为了看起来更像是个报告,我们提供了html转pdf的功能。这里使用的是html2canvas的前端技术。html2canvas库的基本原理是读取网页上的目标DOM节点的信息来绘制canvas,然后生成图片,具体的使用过程中也会有一些问题,这里的工作主要是前端的小伙伴在执行,我这里了解的就有限了。

    然而,我们的产品经理怎么可能这么容易放过我们,经过几次“推脱”,最终我们还是要接下这样一个需求,提供word版报告的下载功能。

    调研及确认方案

    我们后端的技术栈主要是go+php,当然如果必要也可以上python。那么找了一圈发现php其实已经提供了不错的类库,phpword。word文件的生成解决了,下一步要考虑图片的问题,一些常见的图表可以后端绘制,也可以前端绘制,这份工作放到前端还是后端?在经历一番讨论后还是决定继续前端生成,一来是为了保证生成的普通chart图表样式和页面端的展示一致,同时也存在一些比较“花哨”的图表,也能顺利放到word中去。

    “花哨”的图表


    最终讨论的流程图如下:

    流程图

    万事俱备,开始干活。

    phpword使用TIPS

    都是比较成熟的类库,网上有非常多的资料,这里就把开发中可能会遇见的问题列一下,以供参考。

    程序从头开始编写word

    $phpWord = new \PhpOffice\PhpWord\PhpWord();

    $section = $phpWord->addSection(); 

    $section->addTitle("$title", 我是标题);//从这里开始就是一点点的构造文件了,文件的排版样式等等都可调节

    使用模板生成word

    手动调节实在太累,稍微复杂点的页面光一点点调节代码就累死人了,所以phpword也提供模板替换的方式

    $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('./template.docx');

    $templateProcessor->setValue('title','我是标题');  //文字替换

    $img = array("path" => './123.jpg', "width" => 300, "height" => 300);// $templateProcessor->setImageValue('img',$img); // 图片替换


    word模板

    具体对应到word文件就像图片中一样,这样就可以随意排版了。

    但是模板替换也有一些不方便的地方,比如不能任意新增和删除某个元素。

    那么如果有的列表或模块需要动态修改,怎么办?

    这里提供了两种方法。

    $templateProcessor->cloneRow("row1",1); //对于table类型,可以动态进行操作

    对于一整块数据

    $templateProcessor->cloneBlock("A_BLOCK",1); // 这里可以预先在模板中设置好block,对于删除操作,原本还提供了deleteBlock()方法,但是执行后的word会报错而无法打开,替代方案就是 cloneBlock("A_BLOCK",0);


    block示例



    这里还需要注意的一点是,所有的方法本质都是通过正则匹配查找关键词进行替换,这里使用的还是递归匹配,所以如果文件很大,那么可能会消耗内存过大而报错。

    我开发的时候还出现了另外一种问题,本地php版本7.2.9,没有bug,但是服务器上的php版本是php7.0,在使用cloneBlock的时候没有反应,追溯源码发现在正则匹配的位置根本无法匹配成功,这应该是preg_match在7.0版本下的bug,具体原因还没追到。

    那么这个时候怎么办?在一番寻找后,找到了一个非官方的解决方式。

    class TemplateProcessorMod extends \PhpOffice\PhpWord\TemplateProcessor { public function __construct($documentTemplate){ parent::__construct($documentTemplate); } public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null) { $cloneXML = ''; $replaceXML = null; // location of blockname open tag $startPosition = strpos($this->tempDocumentMainPart, '${' . $blockname . '}'); if ($startPosition) { // start position of area to be replaced, this is from the start of the <w:p before the blockname $startReplacePosition = strrpos($this->tempDocumentMainPart, '<w:p ', -(strlen($this->tempDocumentMainPart) - $startPosition)); // start position of text we're going to clone, from after the </w:p> after the blockname $startClonePosition = strpos($this->tempDocumentMainPart, '</w:p>', $startPosition) + strlen('</w:p>'); // location of the blockname close tag $endPosition = strpos($this->tempDocumentMainPart, '${/' . $blockname . '}'); if ($endPosition) { // end position of the area to be replaced, to the end of the </w:p> after the close blockname $endReplace = strpos($this->tempDocumentMainPart, '</w:p>', $endPosition) + strlen('</w:p>'); // end position of the text we're cloning, from the start of the <w:p before the close blockname $endClone = strrpos($this->tempDocumentMainPart, '<w:p ', -(strlen($this->tempDocumentMainPart) - $endPosition)); $cloneLength = ($endClone - $startClonePosition); $replaceLength = ($endReplace - $startReplacePosition); $cloneXML = substr($this->tempDocumentMainPart, $startClonePosition, $cloneLength); $replaceXML = substr($this->tempDocumentMainPart, $startReplacePosition, $replaceLength); } } if ($replaceXML != null) { $cloned = array(); for ($i = 1; $i <= $clones; $i++) { $cloned[] = $cloneXML; } if ($replace) { $this->tempDocumentMainPart = str_replace($replaceXML, implode('', $cloned), $this->tempDocumentMainPart); } } return $cloneXML; }}

    通过继承的方式重写cloneBlock方法,然后通过字符串替换的方式取代正则匹配,这种方式的效率确实不如正则,但是却能很好的解决问题。

    图片添加水印

    我们还有一个需求,就是生成的图片要添加水印,这里就一起记录在这里。

    首先添加水印的类库使用GD库

    $img = imagecreatefromstring(file_get_contents($file)); //读取原始文件

    $style = imagecolorallocatealpha($img,220, 220, 220,100); //水印的颜色和验证码,这里需要注意,最后一个参数不是透明度的百分比

    /* A value between 0 and 127.* 0 indicates completely opaque while * 127 indicates completely transparent.* /这个是原始注释,是0到127,这里需要注意一下

    imagettftext($img, $font_size, $font_angle, $i, $j, $style, $font, $content); //给图片加水印,里面的参数分别是,图片,文字大小,旋转角度,x轴位置,y轴位置,文字样式,字体文件路径,文字内容

    imagejpeg($img,$newFilePath); //生成文件

    imagedestroy($img); //销毁内存中的图片

    这里还需要注意下,使用拥有商用版权的字体,小心给自己的公司添麻烦。


    相关文章

      网友评论

          本文标题:如何实现图文word生成

          本文链接:https://www.haomeiwen.com/subject/diojxhtx.html