phpword插件导出word文件时中文乱码问题处理方案


Posted in PHP onAugust 19, 2014

最近一个项目开发要用到PHP技术导出Word文档,比较了几种方案,首先是使用Microsoft Office自带的ActiveX/COM组件,比如Word.Application,这种方式的优点是格式兼容度高,可以生成纯doc的Word2003格式文档,缺点一是比较占资源(调用会启动一个WINWORD.EXE进程),不适合Web多用户访问使用;二是PHP这种Web开发技术大多数是跑在Linux服务器上,当然也就无法使用Windows下的技术了,平台可移植和兼容性不好。

第二种生成Word的方案是生成Word兼容的网页格式,然后以Word方式打开,这种方案总体上感觉怪怪的,毕竟文件格式是HTML的,而且格式兼容度不好,不过这种方式的优点是节省服务器资源,能够快速生成;最后一种方案也就是今天的主角,采用PHPWord生成Word2007(docx)格式的文档,现在基本上微软Office Word 2003以后的版本均兼容这种格式了,对于2003版本来说,仅需要下载安装个兼容格式包(下载地址),也能正常打开这类文件,当然如果你使用的是最新版本的Office(包括但不限于Office 2007、Office 2010)则不需要安装此格式包。

好了,下面我就介绍一下PHPWord,大家可以通过访问项目主页下载并获得关于项目的更多信息。

我在使用过程中主要遇到了中文乱码的问题,结合网上大神们的指导,通过下面的方式解决了这类问题,希望对大家有所帮助。

1、增加东亚字体支持 

打开并编辑路径/Writer/Word2007/Base.php文件内容,大概在第349行(行数随着版本可能会有变化)大概函数_writeTextStyle内添加:

$objWriter->writeAttribute('w:eastAsia', $font)
比如我的修改片段基本是下面这样:

// 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', '中文'));

上面的代码主要解决模板的问题,下面同样的道理,解决Section添加文本的问题,找到代码$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;
}

调用方式和上面的模板调用大同小异,这边就不列举了。

折腾了这么多,突然发现网上还有另外一个版本的PhpWord,项目类名大小写上略有不同,隶属于PHPOffice/PHPWord,GitHub项目地址(文档)。这个版本的PHPWord内容更加丰富,支持的功能也比较多(包括行间距,缩进和首行缩进等),最后我也采取的这个版本的PHPWord,值得注意的是这两个版本的PHPWord在API接口上基本一致,可以通用。但是有些API,在PHPOffice/PHPWord里是不推荐的,比如createSection需要改成addSection,另外应用这个版本的PHPWord不需要像上面那样做任何中文支持的修改,比较省事。

这两个PHPWord项目的官方都提供了较详细的使用例子和文档,这里就不介绍了。最后提示的是:在模板模式下loadTemplate,只能使用setValue等模板操作方法,不能再添加段落或者段落修改了。这个略有不便。

对于PHPOffice/PHPWord我提供一个简单的例子供参考(当然官方例子更多):

require_once 'PhpOffice/PhpWord/PhpWord.php'; // 包含头文件
use PhpOffice\PhpWord\Autoloader;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\IOFactory;
 
require_once __DIR__ . '/PhpOffice/PhpWord/Autoloader.php';
Autoloader::register();
Settings::loadConfig();
 
// Create a new PHPWord Object
$PHPWord = new \PhpOffice\PhpWord\PhpWord();
$PHPWordHelper= new \PhpOffice\PhpWord\Shared\Font();
 
$PHPWord->setDefaultFontName('仿宋'); // 全局字体
$PHPWord->setDefaultFontSize(16);   // 全局字号为3号
 
// 设置文档的属性,这些在对文档右击属性可以看到,也可以省去这些步骤
$properties = $PHPWord->getDocumentProperties();
$properties->setCreator('张三');  // 创建者
$properties->setCompany('某公司'); // 公司
$properties->setTitle('某某文档'); // 标题
$properties->setDescription('http://wangye.org'); // 描述
$properties->setLastModifiedBy('李四'); // 最后修改
$properties->setCreated( time() );   // 创建时间
$properties->setModified( time() );   // 修改时间
 
// 添加3号仿宋字体到'FangSong16pt'留着下面使用
$PHPWord->addFontStyle('FangSong16pt', array('name'=>'仿宋', 'size'=>16));
 
// 添加段落样式到'Normal'以备下面使用
$PHPWord->addParagraphStyle(
 'Normal',array(
  'align'=>'both',
  'spaceBefore' => 0,
  'spaceAfter' => 0,
  'spacing'=>$PHPWordHelper->pointSizeToTwips(2.8),
  'lineHeight' => 1.19, // 行间距
  'indentation' => array( // 首行缩进
   'firstLine' => $PHPWordHelper->pointSizeToTwips(32)
  )
 )
);
 
// Section样式:上3.5厘米、下3.8厘米、左3厘米、右3厘米,页脚3厘米
// 注意这里厘米(centimeter)要转换为twips单位
$sectionStyle = array(
  'orientation' => null,
  'marginLeft' => $PHPWordHelper->centimeterSizeToTwips(3),
  'marginRight' => $PHPWordHelper->centimeterSizeToTwips(3),
  'marginTop' => $PHPWordHelper->centimeterSizeToTwips(3.5),
  'marginBottom' => $PHPWordHelper->centimeterSizeToTwips(3.8),
  'pageNumberingStart' => 1, // 页码从1开始
  'footerHeight' => $PHPWordHelper->centimeterSizeToTwips(3),
);
 
$section = $PHPWord->addSection($sectionStyle); // 添加一节
 
// 下面这句是输入文档内容,注意这里用到了刚才我们添加的
// 字体样式FangSong16pt和段落样式Normal
$section->addText('文档内容', 'FangSong16pt', 'Normal');
$section->addTextBreak(1); // 新起一个空白段落
 
$objWriter = IOFactory::createWriter($PHPWord, 'Word2007');
$objWriter->save('/path/to/file'); // 保存到/path/to/file路径下

总结

1、用模板word生成word中文乱码解决方案:打开phpword/Template.php文件,找到$replace = utf8_encode($replace);将其改为$replace =iconv('gbk', 'utf-8', $replace); 即可。

2、直接生成word文档,调用addText对象时中文乱码解决方案:打开phpword/Section.php文件,找到$givenText = utf8_encode($text);将其改为$givenText = iconv('gbk', 'utf-8', $text);即可。

3、貌似其他方法也类似第解决。

4、注意php文件采用gbk哦。反正我的显示中文了。在网上找了好久,研究了半天才搞定。

PHP 相关文章推荐
PHP COOKIE设置为浏览器进程
Jun 21 PHP
php 文件上传后缀名与文件类型对照表(几乎涵盖所有文件)
May 16 PHP
国外PHP程序员的13个好习惯小结
Feb 20 PHP
php 计划任务 检测用户连接状态
Mar 29 PHP
CI(CodeIgniter)框架中的增删改查操作
Jun 10 PHP
php中使用url传递数组的方法
Feb 11 PHP
详解WordPress开发中过滤属性以及Sql语句的函数使用
Dec 25 PHP
php实现简单爬虫的开发
Mar 28 PHP
CodeIgniter 完美解决URL含有中文字符串
May 13 PHP
mysql查找删除重复数据并只保留一条实例详解
Sep 24 PHP
PHP/HTML混写的四种方式总结
Feb 27 PHP
PHP大文件切割上传并带进度条功能示例
Jul 01 PHP
PHP防止表单重复提交的几种常用方法汇总
Aug 19 #PHP
PHP中单引号与双引号的区别分析
Aug 19 #PHP
php版本的cron定时任务执行器使用实例
Aug 19 #PHP
PHP框架Swoole定时器Timer特性分析
Aug 19 #PHP
几道坑人的PHP面试题 试试看看你会不会也中招
Aug 19 #PHP
PHP针对常规模板引擎中与CSS/JSON冲突的解决方法
Aug 19 #PHP
php解析json数据实例
Aug 19 #PHP
You might like
Drupal7连接多个数据库及常见问题解决
2014/03/02 PHP
php命令行(cli)下执行PHP脚本文件的相对路径的问题解决方法
2015/05/25 PHP
PHP实现的简单缓存类
2015/07/29 PHP
PHP文件及文件夹操作之创建、删除、移动、复制
2016/07/13 PHP
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
2019/10/16 PHP
Javascript 学习笔记 错误处理
2009/07/30 Javascript
JSQL SQLProxy 的 php 版本代码
2010/05/05 Javascript
JS 图片缩放效果代码
2010/06/09 Javascript
Javascript实现仿WebQQ界面的“浮云”兼容 IE7以上版本及FF
2011/04/27 Javascript
UI Events 用户界面事件
2012/06/27 Javascript
JavaScript 在网页上单击鼠标的地方显示层及关闭层
2012/12/30 Javascript
javascript实现dom动态创建省市纵向列表菜单的方法
2015/05/14 Javascript
Jquery attr()方法 属性赋值和属性获取详解
2016/04/15 Javascript
js获取元素的标签名实现方法
2016/10/08 Javascript
如何提高Dom访问速度
2017/01/05 Javascript
Angular利用trackBy提升性能的方法
2018/01/26 Javascript
vue-cli2.9.3 详细教程
2018/04/23 Javascript
JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例
2018/07/27 Javascript
Node.js搭建WEB服务器的示例代码
2018/08/15 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
javascript实现拖拽碰撞检测
2020/03/12 Javascript
[02:17]TI4西雅图DOTA2前线报道 啸天mik夫妻档解说
2014/07/08 DOTA
python打开文件并获取文件相关属性的方法
2015/04/23 Python
python使用threading获取线程函数返回值的实现方法
2017/11/15 Python
python中not、and和or的优先级与详细用法介绍
2020/11/03 Python
CSS3模拟IOS滑动开关效果
2016/09/28 HTML / CSS
基于HTML5 Canvas 实现弹出框效果
2017/06/05 HTML / CSS
html5的canvas方法使用指南
2014/12/15 HTML / CSS
JBL澳大利亚官方商店:扬声器、耳机和音响系统
2018/05/24 全球购物
美赞臣新加坡官方旗舰店:Enfagrow新加坡
2019/05/15 全球购物
CSMA/CD介质访问控制协议
2015/11/17 面试题
JDBC操作数据库的基本流程是什么
2014/10/28 面试题
执行力心得体会
2013/12/31 职场文书
学生会辞职信
2015/03/02 职场文书
本科毕业论文致谢怎么写
2015/05/14 职场文书
员工给公司的建议书
2019/06/24 职场文书