Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 721|回复: 0

PHPWord中文乱码、单元格合并、动态表格模板解决方案合集

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-6-30 22:23:33 | 显示全部楼层 |阅读模式

    摘要  最近一个项目开发要用到PHP技术导出Word文档,采用PHPWord插件,版本为0.6.2 beta,CodePlex已停止维护。网上还有另外一个版本的PhpWord,项目类名大小写上略有不同,隶属于PHPOffice/PHPWord,GitHub项目地址。这个版本的PHPWord为CodePlex停止维护后添加,目前更新至0.15,个人觉得0.12作者更新的Release较为实用,此项目内容更加丰富,支持的功能也比较多(包括行间距,缩进和首行缩进等)。但是有些API,在PHPOffice/PHPWord里是不推荐的,比如createSection需要改成addSection,另外应用这个版本的PHPWord不需要像PHPWord 0.6.2那样做任何中文支持的修改。本文重点就PHPWord 0.6.2 作一介绍。

    1、增加东亚字体支持

    打开/Writer/Word2007/Base.php文件,大概在第349行,函数_writeTextStyle内添加:

    $objWriter->writeAttribute('w:eastAsia', $font)

    修改后的内容如下:

    if($font != 'Arial') { $objWriter->startElement('w:rFonts'); $objWriter->writeAttribute('w:eastAsia', $font); // 添加这行 $objWriter->writeAttribute('w:ascii', $font); $objWriter->writeAttribute('w:hAnsi', $font); $objWriter->writeAttribute('w:cs', $font); $objWriter->endElement(); }

    2、默认模板中文乱码(此模板后面会修改,不推荐此方法)

    打开/PHPWord/Template.php,找到代码$replace = utf8_encode($replace);修正为$replace = iconv( 'gbk','utf-8', $replace);代码如下:

    /** * Set a Template value * * @param mixed $search * @param mixed $replace */ public function setValue($search, $replace) { if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') { $search = '${'.$search.'}'; } if(!is_array($replace)) { //$replace = utf8_encode($replace); $replace =iconv('gbk', 'utf-8', $replace); // 注释掉上面行后添加这行  } $this->_documentXML = str_replace($search, $replace, $this->_documentXML); }

    中文调用方式也要修改:

    $document->setValue('Template', iconv('utf-8', 'GB2312//IGNORE', '中文'));

    3、中文乱码问题

    打开/PHPWord/Section.php,找到代码$givenText = utf8_encode($text);修改为$givenText = iconv('gbk', 'utf-8', $text);代码如下:

    /** * Add a Text Element * * @param string $text * @param mixed $styleFont * @param mixed $styleParagraph * @return PHPWord_Section_Text */ public function addText($text, $styleFont = null, $styleParagraph = null) { //$givenText = utf8_encode($text); $givenText = iconv('gbk', 'utf-8', $text); // 注释掉上面行后添加这行 $text = new PHPWord_Section_Text($givenText, $styleFont, $styleParagraph); $this->_elementCollection[] = $text; return $text; }

    替换Section.php文件所有utf8_encode($参数)函数为iconv('gbk','utf-8',$参数)

    同理修改/PHPWord/Section目录下Header.php、Footer.php、TextRun.php、Table/Cell.php

    其中TextRun.php是防止文本资源(段落连续)中文错误,Cell.php是防止表格中文错误。重点是addText函数。

    调用方式修改为:

    $section->addText(iconv('utf-8','GBK//IGNORE','中文'));

    3、单元格合并问题(类colspan和rowspan)

    打开PHPWord/Style/Cell.php,增加两个私有属性

    private $_rowMerge = null; private $_cellMerge = null;

    构造函数初始化赋值null

    $this->_rowMerge=null; $this->_cellMerge=null;

     

    同文件,增加如下方法

    public function getRowMerge() { return $this->_rowMerge; } public function setRowMerge($pValue = null) { $this->_rowMerge = $pValue; return $this; } public function getCellMerge() { return $this->_cellMerge; } public function setCellMerge($pValue = null) { $this->_cellMerge = $pValue; return $this; } 

    编辑PHPWord/Writer/Word2007/Base.php,修改函数_writeCellStyle,$styles增加新属性判断

     

    $rowMerge = $style->getRowMerge(); $cellMerge = $style->getCellMerge(); //$styles = (!is_null($bgColor) || !is_null($valign) || !is_null($textDir) || $borders) ? true : false; $styles = (!is_null($bgColor) || !is_null($valign) || !is_null($textDir) || $borders || !is_null($rowMerge) || !is_null($cellMerge)) ? true : false;

     

    修改之后的if($styles)判断条件,增加单元格合并内容判断:

    if (!is_null($cellMerge)) { $objWriter->startElement('w:gridSpan'); if ((string)$cellMerge !== 'continue') { $objWriter->writeAttribute('w:val', $cellMerge); } $objWriter->endElement(); } if (!is_null($rowMerge)) { $objWriter->startElement('w:vMerge'); if ((string)$rowMerge !== 'continue') { $objWriter->writeAttribute('w:val', $rowMerge); } $objWriter->endElement(); }

    Rowspan调用方式

     

    $table1->addCell(2000,array('rowMerge' => 'restart'))->addText(iconv('utf-8','GBK//IGNORE','中文'));//需要合并的第一行 $table1->addCell(2000,array('rowMerge' => 'continue')); //需要合并的其余行,有几行需要复制几行,一般是放循环里面

    Colspan调用方式比价简单

    $table1->addCell(2000,array('cellMerge' => 'restart'))->addText(iconv('utf-8','GBK//IGNORE','中文'));//直接通过cell宽度控制即可

    5、模板动态生成表格

    默认导入模板之后,只能setValue,不能再增加行或文字

    但一般表格文件均为动态行,/PHPWord/Template.php文件不再满足要求

    CloneRow提供了一个解决方案,GitHub项目地址

    /** * Set a Template value * * @param mixed $search * @param mixed $replace */ public function setValue($search, $replace, $limit=-1) { //修改此函数 if(substr($search, 0, 1) !== '{' && substr($search, -1) !== '}') { $search = '{'.$search.'}'; } preg_match_all('/\{[^}]+\}/', $this->_documentXML, $matches); foreach ($matches[0] as $k => $match) { $no_tag = strip_tags($match); if ($no_tag == $search) { $match = '{'.$match.'}'; $this->_documentXML = preg_replace($match, $replace, $this->_documentXML, $limit); if ($limit == 1) { break; } } } } /** * Clone Rows in tables * * @param string $search * @param array $data */ public function cloneRow($search, $data=array()) {//新增如下两函数 // remove ooxml-tags inside pattern foreach ($data as $nn => $fieldset) { foreach ($fieldset as $field => $val) { $key = '{'.$search.'.'.$field.'}'; $this->setValue($key, $key, 1); } } // how many clons we need $numberOfClones = 0; if (is_array($data)) { foreach ($data as $colName => $dataArr) { if (is_array($dataArr)) { $c = count($dataArr); if ($c > $numberOfClones) $numberOfClones = $c; } } } if ($numberOfClones > 0) { // read document as XML $xml = DOMDocument::loadXML($this->_documentXML, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING); // search for tables $tables = $xml->getElementsByTagName('tbl'); foreach ($tables as $table) { $text = $table->textContent; // search for pattern. Like {TBL1. if (mb_strpos($text, '{'.$search.'.') !== false) { // search row for clone $patterns = array(); $rows = $table->getElementsByTagName('tr'); $isUpdate = false; $isFind = false; foreach ($rows as $row) { $text = $row->textContent; $TextWithTags = $xml->saveXML($row); if ( mb_strpos($text, '{'.$search.'.') !== false // Pattern found in this row  OR (mb_strpos($TextWithTags, '<w:vMerge/>') !== false AND $isFind) 
                    // This row is merged with upper row (Upper row have pattern) ) { // This row need to clone $patterns[] = $row->cloneNode(true); $isFind = true; } else { // This row don't have any patterns. It's table header or footer if (!$isUpdate and $isFind) { // This is table footer // Insert new rows before footer $this->InsertNewRows($table, $patterns, $row, $numberOfClones); $isUpdate = true; } } } // if table without footer if (!$isUpdate and $isFind) { $this->InsertNewRows($table, $patterns, $row, $numberOfClones); } } } // save document $res_string = $xml->saveXML(); $this->_documentXML = $res_string; // parsing data foreach ($data as $colName => $dataArr) { $pattern = '{' . $search . '.' . $colName . '}'; foreach ($dataArr as $value) { $this->setValue($pattern, $value, 1); } } } } /** * Insert new rows in table * * @param object &$table * @param object $patterns * @param object $row * @param int $numberOfClones */ protected function InsertNewRows(&$table, $patterns, $row, $numberOfClones) { for ($i = 1; $i < $numberOfClones; $i++) { foreach ($patterns as $pattern) { $new_row = $pattern->cloneNode(true); $table->insertBefore($new_row, $row); } } } }

    请注意,此setValue函数与2不同,那么问题来了,需要进行中文编码转换

    if(!is_array($replace)) {
      $replace =iconv('gbk', 'utf-8', $replace); }

    运行中,新版本PHP大概5.4之后会提示( ! ) Deprecated: Non-static method DOMDocument::loadXML() should not be called statically, assuming $this from incompatible context in /PHPWord/Template.php on line 168

    非静态函数不能直接采用类名::方法的方式调用,DOMDocument::loadXML,可按如下修改,也可直接前面@注释掉错误提示即可。

    $xml = new DOMDocument(); $xml->loadXML($this->_documentXML, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);

    至此,旧版本PHPWord所有中文问题和表格合并及模板动态表格问题已OK。

    注意:所有中文调用均需要iconv转换

         关注/PHPWord/Examples,有文本,表格,图片,链接,对象等等操作实例

             采用更新版本的PHPWord,无中文问题,样式定义更方便,抽空再单独介绍

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-2 21:12 , Processed in 0.071346 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表