文件预览的效果图(附带导出pdf文件和打印功能):
实现方式如下:
1.打开模板word文件 , 在对应地方打上书签;
2.在php.ini中加上 extension=php_com_dotnet.dll ; 目前只支持windows系统
3.代码如下
用数组来记录书签和表字段的对应关系 如果经常变动也可以用数据库来记录
'PROJECT_MAP' => [
//用数组来记录书签和表字段的对应关系 如果经常变动也可以用数据库来记录
'id' => 'id',
'project_name' => 'name',
'project_type' => 'ptype_name',
'user_department' => 'user_department',
'project_code' => 'project_code',
'lianxiren' => 're_man',
'lianxi_telephone' => 're_phone',
],
public function preview()
{
$pid = I('pid');
$type = I('type');
$map = $this->getMap($pid, $type);
//getmap方法就是获取上方project_map形式的一个数组
$tempPath = '/upload/project/template/'.$type.'.doc';
//模板文件的路径及文件名(我的文件名是英文,中文转码)
$word = new \Lib\Word(); //注意命名空间
list($ret, $mix) = $word->createDocUseComFromTemp($map, $tempPath, $filename);
if (!$ret) {
exit($mix);
}
$this->preview($mix,$pid);
//因为预览文件大概需要5-6秒的时间,这里加入缓存机制 , 如果不带id 会根据模板文件名来生成缓存文件 ,
//就会导致不同项目预览同一个模板时 会调用同一个缓存文件, 所以把pid拼接到文件名中,即不同项目id生成不同的缓存文件
}
public function getMap($pid, $type)//获取书签对应映射的值
{
$project = M('yy_project')->where("id=$pid")->find();
//处理数据
$project['procect_sn'] = end(explode('-', $project['code']));
$map = C('PROJECT_MAP');
foreach ($map as $k => &$v) {
$v = $project[$v];
}
//对数据的处理在这里进行
//图片传入路径就行 可以实现签名功能
return array_filter($map);
}
public function preview($word_path = '' ,$pid = null)
{
if (!$word_path) echo CLNG('param_error');
$fileExt = explode('.', $word_path)[1] ?: 'doc';
if (!$fileExt) echo '需要预览的word不是合法的.doc或者.docx';
$word_path = get_absolute_path($word_path, true);//获取文件的绝对路径 方法自己封装
//这里会检测是否是合法路径
try {
$Word = new \Lib\Word();
$Word->dcomPreview($word_path,true,$pid);//使用DCOM组件,服务器需要安装word
} catch (\Think\Exception $e) {
echo '预览失败:'.$e->getMessage();
}
}
public function download($word_path = '', $file_name = '', $ext = '')
{
// $word_path = $word_path ?: 'D:/test.doc';//for debug;
$word_path || $this->ccerror(CLNG('param_error'));
$Word = new \Lib\Word();
$Word->download($word_path, $file_name, $ext);
}
word.class.php:
SaveAs() 只能保存doc 或者docx文件
* ActiveDocument->ExportAsFixedFormat() 导出pdf文件,第一个参数传入pdf文件完成文件名即可,.html等其他格式的文件不知是否也是如此导出TOTST
*/
namespace Lib;
use Lib\OpenOffice2Pdf as OO2Pdf;
class Word
{
const MS_WORD_COM_SERVICE_NAME = 'word.application';
const MS_WORD_DCOM_NAME = 'Microsoft Word 97-2003 Document'; //ms_word的DCOM组件的名称
const PDF_HEADER = 'Content-type:application/pdf';
/**
* 使用phpword预览word的实现 主要针对Word2007(.docx)以上有效
* 思路:加载.docx phpword转换成 .html 输出html给浏览器 浏览器加载html实现预览 可能部分样式会有出入.
*
* @param string $inputFileName word文件路径
* @param string $inputFileType word文件的类型(Word2007,MsDoc,参见PHPWord源码)
*
* @return string HTML content-type=application/html
*/
public function phpWordPreview($inputFileName = '', $inputFileType = '')
{
// $inputFileName = $inputFileName ?: 'D:/test.docx'; //for debug.
vendor('autoload', '', '.php'); //引入自动加载类,用于加载第三方类库
if (!$inputFileType) {
$file_ext = explode('.', $inputFileName)[1];
if ('docx' == $file_ext) {
$inputFileType = 'Word2007'; //Word2007及以上
} else {
// $this->openOfficePreview($inputFileName);//调用openoffice处理吧
$inputFileType = 'MsDoc'; //Word97-2003 但是会乱码 何解? TODO
}
}
$objReader = \PhpOffice\PhpWord\IOFactory::createReader($inputFileType);
$objPHPFile = $objReader->load($inputFileName);
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($objPHPFile, 'HTML');
// $objWriter->save('D:/11.doc');//可用Word2007 writer 写出word2003文档 NOTE
$objWriter->save('php://output'); //直接输出html内容,前端用iframe承载
}
/**
* 使用windows服务器上的DCOM组件,将文档打开另存为pdf,然后再读取该文件pdf输出给浏览器.
* NOTE:支持.doc和.docx,但是依赖windows服务器且安装了office办公软件.且需要word2007+以上 NOTE.
*/
public function dcomPreview($wordPath = '', $show = true, $pid = null)
{
//如果有生成的对应的临时文件,则直接输出即可,而不用加载耗时的COM组件
// exit($wordPath);
$fileName = explode('.', end(explode('/', $wordPath)))[0]; //word文件名
// exit($fileName);
$pdfSavePath = get_temp_file_name($fileName.$pid, 'pdf'); //获取临时文件名的完整文件名
if (file_exists($pdfSavePath)) {
//也是有效期,否则旧文件一直在,导致有问题
if (filemtime($pdfSavePath) + C('MAPPING_TEMP_FILE_EXPIRE') >= time()) {
if (!$show) {
return $pdfSavePath;
exit;
}
// // echo $pdfSavePath;exit;
// $fileName = basename($pdfSavePath);
// //做一个软链接
// // echo ENTRY_PATH . '\\' . $fileName .PHP_EOL;
// $pdfSavePath = str_replace('/', '\\', $pdfSavePath);
// // echo $pdfSavePath.PHP_EOL;
// // echo $runtimePath = realpath(RUNTIME_PATH .'/Cache');exit;
// $runtimePath = realpath(RUNTIME_PATH .'/Cache');
// exec('mklink ' .$runtimePath . '\\' . $fileName . ' '.$pdfSavePath, $output);
// // print_r($output);
// //定位到pdf文件的软链接
// $fileName = 'http://'.$_SERVER['HTTP_HOST'].'/../app/Runtime/Cache/'. $fileName;
// header('Location:'.$fileName);
// exit;
header(self::PDF_HEADER);
readfile($pdfSavePath);
exit;
}
}
//调用COM组件导出新的PDF文档
// if (!$this->checkCom()) {
// return [false, 'DCOM组件拓展未开启或者服务器不支持DCOM组件服务!'];
// }
$word = new \COM(self::MS_WORD_COM_SERVICE_NAME, null, CP_UTF8);
if (!$word) {
return [false, '调用\''.self::MS_WORD_DCOM_NAME.'\'DCOM组件失败!'];
}
$word->Visible = 0; //是否在前端显示 1(是) 0(否),即是否显示打开word app
// recommend to set to 0, disables alerts like "Do you want MS Word to be the default .. etc"
$word->DisplayAlerts = 0; //隐藏弹出窗
// $wordPath = str_replace('\\', '/', $wordPath);
$wordPath = get_absolute_path($wordPath); //获取绝对路径
if (!file_exists($wordPath)) {
return [false, 'word文件\''.$wordPath.'\'不存在'];
}
$result = $word->Documents->Open($wordPath); //打开一个文档 返回的结果为null则打开失败 open需要打开一个绝对路径
if (!$result) {
//如果打开失败,则关闭打开句柄,节约服务器资源
$this->quitWord($word);
// $word->Quit(false); //必须,否则下次实例化时会提示未初始化“尚未调用Colinitilize” Quit(false)表示不保存任何更改
// $word = null; //free the object
// unset($word);
return [false, '打开word文档失败,请检测服务器用户是否有权操作\''.self::MS_WORD_DCOM_NAME.'\' DCOM组件!'];
} else {
//貌似ExportAsFixedFormat是word2007新增的功能,也就是说服务器安装的如果是word2003则无法使用该方法
$word->ActiveDocument->ExportAsFixedFormat($pdfSavePath, 17, false, 0, 0, 0, 0, 7, true, true, 2, true, true, false);
// $activeDoc->Close(false); //关闭当前活动文档,不保存所做更改(即不修改模板) NOTE
$this->quitWord($word);
// $word->Quit();
// $word = null;
// unset($word);
if (!$show) {
return $pdfSavePath;
exit;
}
header(self::PDF_HEADER);
readfile($pdfSavePath);
exit;
}
}
/**
* openoffice预览word 主要针对PHPWord不能处理.doc的补充 所以主要针对.doc文档的预览实现
* 思路:加载word->openoffice转换word为pdf->保存pdf至临时目录->php读取该pdf文件->输出application/pdf类型的文档为前端->浏览器识别pdf类型文件实现查看.
*
* @param $wordPath word文档的路径
* @param $pdfSavePath 临时pdf的保存完整文件名
*
* @throws Exception
*/
public function openOfficePreview($wordPath = '')
{
// $wordPath = $wordPath ?: 'D:/test.doc'; //for debug
$fileName = explode('.', end(explode('/', $wordPath)))[0]; //文件名 临时保存的文件名最好不要原来的文件名(有时候是中文),便于后续程序能快速定位
$pdfSavePath = get_temp_file_name($fileName, 'pdf'); //获取临时文件的完整文件名 指定保存到某个临时目录下 NOTE
if (file_exists($pdfSavePath)) {
//NOTE:如果文件存在,则不用重新转换成pdf,而是直接读取
header(self::PDF_HEADER);
readfile($pdfSavePath);
exit;
}
$OO2Pdf = new OO2Pdf();
$pages = $OO2Pdf->run($wordPath, $pdfSavePath); //doc convert to pdf,and save pdf on somewhere.
if ($pages) {
//直接向浏览器返回pdf字符串,浏览器会识别文档类型然后直接打开PDF 间接实现预览功能
header(self::PDF_HEADER); //设置返回内容的标签,便于浏览器识别文件类型
readfile($pdfSavePath); //直接读取临时pdf文件输出
exit;
} else {
\Think\Log::write('生成PDF失败!PDF路径:'.$pdfSavePath);
throw new \Think\Exception('预览word失败!');
}
}
/**
* Word2007+(.docx) 模板的处理 TOTST
* 使用PHPWord替换模板变量字符串保存为word(.docx .doc).
*
* @params array $values 字段名和值的映射数组
* @params string $temp_path 模板路径
* @params string $file_name 根据模板创建的文档的新名称
* @params string $ext 根据模板创建的文档的后缀名 一般为doc docx
*
* @return string 新创建的文档的完整文件名,控制器传入
*/
public function craeteDocUsePhpwordFromTemp(&$map, $temp_path = '', $tempFileName)
{
vendor('autoload', '', '.php'); //引入自动加载类,用于加载第三方类库
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($temp_path);
//循环替换
foreach ($map as $k => $v) {
// $templateProcessor->setValue($k, $v);
$templateProcessor->setValue($k, iconv('utf-8', 'GB2312//IGNORE'), $v); //如果是中文则这样调用?
}
//服务器暂存
$templateProcessor->saveAs($tempFileName); //保存替换变量之后的文档到服务器某个地方
return [true, ''];
}
/**
* Word 97-2003(.doc) 模板的处理
* 使用COM组件,调用"Microsoft Word 97-2003 Document"服务,替换模板中的书签实现模板替换 NOTE
* 要求:
* 1. 服务器系统是Windows
* 2. 服务器系统安装了Word应用程序
* 3. 开启了"Microsoft Word 97-2003"的访问权限 相关配置
* 4. php.ini配置了com_dotnet.dll拓展.
*
* @param pointer $values 映射关联数组
* @param string $tempPath 模板路径
* @param string $tempFileName 生成之后的临时文件名
*
* @return array [bool, 临时文件名];
*/
public function createDocUseComFromTemp(&$values, $tempPath, $tempFileName = '')
{
if (is_win()) {
if (!$this->checkCom()) {
return [false, '请修改php.ini配置,开启com_dotnet拓展!'];
}
$word = new \COM(self::MS_WORD_COM_SERVICE_NAME, null, CP_UTF8);
if (!$word) {
return [false, '调用\''.self::MS_WORD_DCOM_NAME.'\'的COM组件失败!'];
}
$word->Visible = 0; //是否在前端显示 1(是) 0(否),即是否显示打开word app
//打开文档
$tempPath = get_absolute_path($tempPath);
$tempFileName = get_temp_file_name($tempFileName, 'doc');
if (!file_exists($tempPath)) {
return [false, '模板文件\''.$tempPath.'\'不存在'];
}
$result = $word->Documents->Open($tempPath); //返回的结果为null
if (!$result) {
//如果打开失败,则关闭打开句柄,节约服务器资源
$this->quitWord($word);
// $word->Quit(false); //必须,否则下次实例化时会提示未初始化“尚未调用Colinitilize” Quit(false)表示不保存任何更改
// $word = null; //free the object
// unset($word);
return [false, '打开word文档失败,请检测服务器用户是否有权操作\''.self::MS_WORD_DCOM_NAME.'\' DCOM组件!'];
} else {
// //读取文档内容
// $text = $word->ActiveDocument->Content->Text; //Context全部内容,Text貌似是存文本内容 NOTE
// echo $text;
// echo '
';
// //for debug.
// $values = [
// 'year' => date('Y'), //项目编号的年份
// 'sn' => '1203', //项目编号
// 'name' => '测试项目名称', //项目名称
// 'mapping_date' => date('Y-m-d'), //测绘日期
// 'group_leader_name' => '张颖颖', //作业组长
// ];
$activeDoc = $word->ActiveDocument;
//获取书签 替换内容
$BookmarksCount = $activeDoc->Bookmarks->Count; //所有书签数
if ($BookmarksCount < 1) {
//shut down the COM
$this->quitWord($word);
return [false, '不是有效的模板。Word97-2003(.doc)类型的文档模板请使用书签设置占位符!']; //没有一个书签,则认为不是有效的模板
}
//循环替换
for ($i = 1; $i <= $BookmarksCount; ++$i) {
$Bookmark = $activeDoc->Bookmarks->Item($i); //取书签对象
$range = $Bookmark->Range; //书签的输入位置
$labelName = $Bookmark->Name; //书签的名称(与字段名一一对应);
$value = $values[$labelName]; //字符串 或者 数组(通常是表格的数据)
$valueType = 'plain'; //值的类型,默认为纯文本内容
if (!is_array($value)) {
$value = trim($value); //一般是文字文本,但是现在需要支持图片 TODO
} else {
if (is_assoc($value)) {
//需要特殊处理的数组 关联数组
$valueType = 'complex'; //标记为复杂数据
if (isset($value['type'])) {
if ('table' == $value['type']) {
//渲染的是表格数据 TODO
//有data就取data的值,否则就取第一个的值,通常这个值是数据
$data = isset($value['data']) ? $value['data'] : array_shift($value);
if (is_array($data)) {
//DCOM 构建表格 TODO NOTE
//标签是否在表格中
if ($range->Tables->Count) {
$Table = $range->Tables(1);
$renderStartRow = $range->Rows(1);
$renderStartRowIndex = $renderStartRow->Index;
// $Table = $Bookmark->Range->Tables->Item(1);//both ok.
if ($activeDoc->Bookmarks->Exists($labelName.'_end')) {
$tableRenderEndRowIndex = $activeDoc->Bookmarks($labelName.'_end')->Range->Rows(1)->Index; //模板制作者限定的渲染数组的最远边界,该边界下的数据可能就是其它数据了,而他们在同一个表中,而不是所有行都是可以用来渲染数据的,必须有个终点。如果没有人为指定终点,则以最大行为终点。
} else {
$tableRenderEndRowIndex = $Table->Rows->Count;
}
$tableRenderMaxRows = $tableRenderEndRowIndex - $renderStartRowIndex + 1;
if (($dataRows = count($data)) > $tableRenderMaxRows) {
for ($j = 0, $more = $dataRows - $tableRenderMaxRows; $j < $more; ++$j) {
$Table->Rows->Add($renderStartRow);
}
}
$tableMaxColumn = $Table->Columns->Count; //XXX 不支持复杂表格的列的数据渲染,默默认为表格的列数就是数据可以渲染的列
foreach ($data as $k => $item) {
$currentDataMaxColumn = count($item);
for ($i = 1; $i <= $tableMaxColumn; ++$i) {
if ($i > $currentDataMaxColumn) {
break;
}
$Table->Cell($renderStartRowIndex + $k, $i)->Range->Text = $item[$i - 1];
}
//if $currentDataMaxColumn > $tableMaxColumn abandon the surplus data.
}
// //for test below.
// $testSaveFile = 'C:\Users\lj\Desktop\cc.doc';
// $word->Documents[1]->SaveAs($testSaveFile);
// $word->Documents->Open($testSaveFile);
// $word->Visible = 1;
} else {
continue; //标签不在表中,则不处理该标签的数据
}
} else {
$valueType = 'plain';
$value = $data;
}
}
} else {
if (1 == count($value)) {
//对应于$value为['xxx'=>[]]的数据
$value = array_values($value); //不管关联数字据的键名,只需要值的内容
$valueType = 'plain'; //标记为复杂数据
$value = implode(' ', $value);
} else {
//暂不支持,有需要请补充逻辑
}
}
} else {
//索引数组承载的多个值,替换成空格连接的字符串
$value = implode(' ', $value);
}
}
//纯文本数据的替换逻辑
if ('plain' == $valueType) {
if (preg_match('/.*\.(jpe?g|png|gif)$/i', $value)) {
$imgUrl = get_absolute_path($value); //服务器端文件绝对路径 NOTE
// echo $imgUrl;//for debug, debug the imgUrl wherther or not exist.
// $this->quitWord($word);
// exit;
//参考C++代码:http://blog.sina.com.cn/s/blog_751b91460102uyde.html
//AddPicture() 参数列表:param1图片地址,param2是否是外部链接,param3是否随word一起保存
// $range->Select();//$range->Select()表示将当前书签的位置选中(光标设置在那里)
// $inlineShape = $word->Selection->InlineShapes->AddPicture($imgUrl, true, true);
//也可以用这种方法,参考:https://msdn.microsoft.com/en-us/library/microsoft.office.interop.word.inlineshapes.addpicture.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
$inlineShape = $word->Selection->InlineShapes->AddPicture($imgUrl, true, true, $range);
$inlineShape->Width = 60; //设置为2:1的图片,for 签名图片 NOTE
$inlineShape->Height = 30;
} else {
$range->Text = $value;
}
}
}
$word->Documents[1]->SaveAs($tempFileName);
$this->quitWord($word);
// $activeDoc->Close(false); //关闭当前活动文档,不保存所做更改(即不修改模板) NOTE
// $word->Quit();
// $word = null;
// unset($word);
return [true, $tempFileName];
}
} elseif (is_linux()) {
return [false, '.doc文档模板的生产文档功能暂不支持Linux服务器!'];
} else {
return [false, '当前服务器不支持word模板处理!'];
}
}
/**
* quit the DCOM of Wrod97-2003.
*
* @param pointer $word
* @param bool $save2Ori save the change to orinal document or not
*/
private static function quitWord(&$word, $save2Ori = false)
{
// $word->ActiveDocument->Close($save2Ori);
$word->Quit($save2Ori);
$word = null;
unset($word);
}
/**
* 检测是否支持DCOM组件.
*/
private function checkCom()
{
//检测php_com_dotnet.dll拓展是否加载了
if (extension_loaded('com_dotnet')) {
return ini_get('com.allow_dcom') > 0; //com.allow_dcom=true是否设置了 注意ini_get()返回值为string
}
return false;
}
/**
* 下载word.
*
* @param $word_path 用于下载的文档路径
* @param $file_name 下载的文件名 可以指定
* @param $ext 下载的文件的拓展字符串,如果未指定,则获取$word_path的文件后缀
*/
public function download($wordPath, $fileName = '', $ext = '')
{
if (!$ext) {
$ext = explode('.', $wordPath)[1];
}
if (@$fp = fopen(get_absolute_path($wordPath, true), 'r')) {
header('Content-Type: application/octet-stream;'); //设置为一个下载类型
header('Accept-Ranges: bytes'); //设置Range,支持断点续传?
header('Accept-Length: '.filesize($wordPath)); //设置内容长度
header('Content-Disposition: attachment; filename='.$fileName.'_'.date('YmdHis').'.'.$ext);
//输出文件内容 输出 load the file to send.
while (!@feof($fp)) {
echo fread($fp, 1024); //一次读取1k ?
}
fclose($fp);
}
// //也可以直接下载 注意application的类型
// header("Content-type:application/msword");
// readfile($inputFileName);
// exit;
}
/**
* 测试COM组件的一些方法.
*/
public function test()
{
$word = new \COM(self::MS_WORD_COM_SERVICE_NAME, null, CP_UTF8);
if (!$word) {
echo '调用\''.self::MS_WORD_DCOM_NAME.'\'的COM组件失败!'.PHP_EOL;
exit;
}
$word->Visible = 0;
$result = $word->Documents->Open('D:\test.doc'); //返回的结果为null
if (!$result) {
echo 'Error'.PHP_EOL;
$this->quitWord($word);
exit;
}
$activeDoc = $word->ActiveDocument;
// echo $activeDoc->Bookmarks->Count . PHP_EOL;
$label = 'label1';
$Bookmark = $activeDoc->Bookmarks('label1');
// var_dump($Bookmark);
//$Bookmark->Range->Rows(1)->isLast isFirst 判断是否是最后一行或者第一行
// echo $tableIndex = $Bookmark->Range->Rows(1)->Index;//当前标签所在的行的第一行在表格中行号 暂不考虑嵌套
// $tableIndex = $Bookmark->Range->Tables(1);//当前标签所在的表格对象
// echo $tableIndex = $Bookmark->Range->Tables->Count;
// echo $tableIndex;
$data = [
[1, '我是第一行第二列内容', '我是第一行第三列内容', 'bbbb', 'dddd', 'deee'],
[2, '我是第二行第二列内容', '我是第二行第三列内容', 'aaa'],
[3, '我是第三行第二列内容', '我是第三行第三列内容', 'ccc'],
];
//标签是否在表格中
if ($Bookmark->Range->Tables->Count) {
$Table = $Bookmark->Range->Tables(1); //默认认为标签所在的表格不是其它表格的子表格或者标签所在的最近的表格就是数据渲染的场所 NOTE
$renderStartRow = $Bookmark->Range->Rows(1);
$renderStartRowIndex = $renderStartRow->Index; //标签所在行的行号,便于操作Cell(rowIndex,cellIndex)去赋值
// $Table = $Bookmark->Range->Tables->Item(1);//both ok.
$tableMaxColumn = $Table->Columns->Count; //表格最大的列数,渲染的数据的列如果大于这个,则舍弃
if ($activeDoc->Bookmarks->Exists($label.'_end')) {
$tableRenderEndRowIndex = $activeDoc->Bookmarks($label.'_end')->Range->Rows(1)->Index; //模板制作者限定的渲染数组的最远边界,该边界下的数据可能就是其它数据了,而他们在同一个表中,而不是所有行都是可以用来渲染数据的,必须有个终点。如果没有人为指定终点,则以最大行为终点。
} else {
$tableRenderEndRowIndex = $Table->Rows->Count;
}
$tableRenderMaxRows = $tableRenderEndRowIndex - $renderStartRowIndex + 1; //表格可渲染的最大行数
//如果要渲染的数据大于可渲染的行数,则新增不足的行
if (($dataRows = count($data)) > $tableRenderMaxRows) {
for ($j = 0, $more = $dataRows - $tableRenderMaxRows; $j < $more; ++$j) {
$Table->Rows->Add($renderStartRow); //在数据渲染起始行后新增一行
}
}
foreach ($data as $k => $item) {
$currentDataMaxColumn = count($item);
for ($i = 1; $i <= $tableMaxColumn; ++$i) {
if ($i > $currentDataMaxColumn) {
break;
}
$Table->Cell($renderStartRowIndex + $k, $i)->Range->Text = $item[$i - 1];
}
//if $currentDataMaxColumn > $tableMaxColumn abandon the surplus data.
}
//for test below.
$testSaveFile = 'C:\Users\lj\Desktop\cc.doc';
$word->Documents[1]->SaveAs($testSaveFile);
$word->Documents->Open($testSaveFile);
$word->Visible = 1;
} else {
echo 'Label selected is not in any table.'.PHP_EOL;
$this->quitWord($word);
}
}
}
修改getmap就行了 , word.class.php就不要改了;