PHPWord中文乱码、单元格合并、动态表格模板解决方案合集
摘要: 最近一个项目开发要用到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,无中文问题,样式定义更方便,抽空再单独介绍
PHPWord中文乱码、单元格合并、动态表格模板解决方案合集的更多相关文章
- JS:jquery插件表格单元格合并.
公司需要用到单元格合并,于是动手封装了一个简单的jquery插件,封装的函数是直接写好转的,请多多提意见看代码是否有优化的地方..... 截图: 代码: /* * mergeTable 0.1 * C ...
- Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等
目录 一.概述 二.效果展示 三.定制表头 1.重写数据源 2.重写QHeaderView 四.设置属性 五.相关文章 原文链接:Qt实现表格控件-支持多级列表头.多级行表头.单元格合并.字体设置等 ...
- 关于table动态添加数据 单元格合并 数组合并
var newArr = [ {"BranchID":1,"BranchName":"城二","BranchFullName&qu ...
- NPOI 教程 - 2.1单元格合并
来源:http://liyingchun343333.blog.163.com/blog/static/3579731620091018212990/ 合并单元格在制作表格时很有用,比如说表格的标题就 ...
- SNF快速开发平台MVC-表格单元格合并组件
1. 表格单元格合并组件 1.1. 效果展示 1.1.1. 页面展现表格合并单元格 图 4.1 1.1.2. 导出excel合并单元格 图 4.2 1.2. 调用说 ...
- excel技巧--单元格合并与拆分
如果要将上图的地区列做成下图的合并单一列: 有如下做法: (以下图表格为例) 1.选择要排序的表格,点击“开始”-->排序和筛选-->自定义排序.在对话框选择“业务项目”进行排序: 2.选 ...
- JTable 单元格合并 【转】
单元格合并 一.单元格合并.(1)我们可以使用Jtable的三个方法:getCellRect(),columnAtPoint(),and rowAtPoint().第一个方法返回一个单元格的边界(Re ...
- ExtJS 4.2 Grid组件的单元格合并
ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ...
- asp.net使用控件datagrid实现表头单元格合并
合并的要点: 1.datagid的单元格合并原理是table中tr,td的布局实现; 2.合并的时机实在其datagridcreate事件中实现; 3.认识一个对象TableCellCollectio ...
随机推荐
- [Objective-C] 016_UI篇_UIView(上)
在我们使用app时屏幕上能看到的UI元素(按钮,列表,图片...),我们称之为视图,都是继承与UIView,它们通常有着位置,大小,背景颜色等属性,在appl中视图和窗口展示了应用的用户界面,同时负责 ...
- Java中的集合(十五) Iterator 和 ListIterator、Enumeration
Java中的集合(十五) Iterator 和 ListIterator.Enumeration 一.Iterator (一).简介 Iterator 是一个接口,它是集合的迭代器.集合可以通过Ite ...
- Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
在数列 a_1, a_2, -, a_n中,定义两个元素 a_i 和 a_j 的距离为 |i-j|+|a_i-a_j|,即元素下标的距离加上元素值的差的绝对值,其中 |x| 表示 x 的绝对值. 给定 ...
- Java实现 蓝桥杯 算法训练 画图(暴力)
试题 算法训练 画图 问题描述 在一个定义了直角坐标系的纸上,画一个(x1,y1)到(x2,y2)的矩形指将横坐标范围从x1到x2,纵坐标范围从y1到y2之间的区域涂上颜色. 下图给出了一个画了两个矩 ...
- Java实现 LeetCode 658 找到 K 个最接近的元素(暴力)
658. 找到 K 个最接近的元素 给定一个排序好的数组,两个整数 k 和 x,从数组中找到最靠近 x(两数之差最小)的 k 个数.返回的结果必须要是按升序排好的.如果有两个数与 x 的差值一样,优先 ...
- Java实现蓝桥杯基础练习特殊回文数
基础练习 特殊回文数 时间限制:1.0s 内存限制:512.0MB 提交此题 锦囊1 锦囊2 问题描述 123321是一个非常特殊的数,它从左边读和从右边读是一样的. 输入一个正整数n, 编程求所有这 ...
- Java实现 LeetCode 475 供暖器
475. 供暖器 冬季已经来临. 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖. 现在,给出位于一条水平线上的房屋和供暖器的位置,找到可以覆盖所有房屋的最小加热半径. 所以,你的输入将会是房 ...
- Java实现蓝桥杯算法提高 哥德巴赫猜想
试题 算法提高 哥德巴赫猜想 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 根据所给函数(判断一个整数是否是素数),然后依托该函数,将输入N内的偶数(6-N),输出为两个素数之和( ...
- Java实现蓝桥杯VIP算法训练 石子游戏
试题 算法训练 石子游戏 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 石子游戏的规则如下: 地上有n堆石子,每次操作可选取两堆石子(石子个数分别为x和y)并将它们合并,操作的得分 ...
- Java 是如何实现跨平台的?
Java 是如何实现跨平台的? 注意:跨平台的是 Java 程序,而不是 JVM.JVM 是用 C/C++ 开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的 JVM 答:我们编写的 ...