使用PHPWord對Word文件作模板替換

因工做須要,使用了版本比較舊的PHPWord項目
官方已不見維護更新,上次版本更新是在Fri Jul 8, 2011 at 8:00 AM
若是PHP版本>=5.3.3,強烈推薦使用PHPOffice/PHPWord這個開源項目
本篇針對的爲舊版本的PHPWordphp

基本安裝

見官網html

問題總結

Autoloader自動加載部分狀況下失敗

在使用Yii 1配置自動加載時沒法正常加載類庫,需對其PHPWord/Autoloader.php作部分調整,這兒借鑑了PHPExcelAutoloader:git

/**
 * PHPWord_Autoloader
 */
class PHPWord_Autoloader
{
    /**
     * Register the Autoloader with SPL
     *
     */
    public static function Register() {
        $functions = spl_autoload_functions();  
        foreach ( $functions as  $function)  
            spl_autoload_unregister($function);  
        $functions = array_merge(array(array('PHPWord_Autoloader','Load')),$functions);  
        foreach ( $functions as $function)  
            $x = spl_autoload_register($function);  
        return $x; 

    }   //    function Register()


    /**
     * Autoload a class identified by name
     *
     * @param    string    $pClassName        Name of the object to load
     */
    public static function Load($pClassName){
        if ((class_exists($pClassName,FALSE)) || (strpos($pClassName, 'PHPWord') !== 0)) {
            //    Either already loaded, or not a PHPWord class request
            return FALSE;
        }

        $pClassFilePath = PHPWORD_BASE_PATH .
                          str_replace('_',DIRECTORY_SEPARATOR,$pClassName) .
                          '.php';

        if ((file_exists($pClassFilePath) === FALSE) || (is_readable($pClassFilePath) === FALSE)) {
            //    Can't load
            return FALSE;
        }

        require($pClassFilePath);
    }   //    function Load()

}

模板替換時沒法識別模板標籤

表現

  1. 使用/複製官方樣例的模板文件替換正常github

  2. 本身手動敲出模板標籤替換異常瀏覽器

緣由

  1. PHPWord的替換規則是將Word文件解析成XML進行替換處理,當Word解析成XML時字符分離了,致使匹配不上模板標籤;app

  2. 具體分析可參考一下資料:ide

解決辦法

參考http://stackoverflow.com/a/21750677/5270710編碼

改進Template類:
可參考Github: Arisse/PHPWord_CloneRowTemplate類進行改造。
由於下面仍須要修改Template類,這兒暫時就不貼代碼了,下面一併貼出改造後的代碼。

中文亂碼

參考CSDN: PHPWord利用模板替換字符串生成精確的word文檔

編輯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);
}

空格輸出

參考CSDN: PhpWord 如何輸出換行符到 word?

在想要輸出換行的地方用<w:br />代替便可.

標記符號輸出

參考百度經驗: 如何在word中選擇打鉤的方框
僅以輸出爲例,其它符號與之相似。
注:PHP文件須要使用UTF-8編碼

  1. Word文件中按照參考文件方式插入

  2. 複製符號到PHP文件;

  3. 正常的輸出替換。
    具體代碼見以下的項目代碼

Template類代碼

// code
/**
* 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.'}';
    }
    
    if(!is_array($replace)) {
        // $replace = utf8_encode($replace);
        // $replace = iconv( 'gbk','utf-8', $replace);
        $replace = str_replace("\n","<w:br />",$replace);
    }

    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;
            }            
        }
    }
}
// code

項目代碼

// @author Heier xheier@outlook.com
public function actionExportPersonTable() {
    // 獲取數據部分代碼
    // ...

    $PHPWord = new PHPWord();
    
    // Word模板目錄
    $personBasePath = Yii::app()->basePath.'/person/';
    
    // 刪除目錄下臨時文件-十分鐘之前
    $this->delfile( $personBasePath, 10 );
    
    // 模板文件名
    $tempName = $personBasePath . '/moban.docx';
    
    $word = $PHPWord->loadTemplate( $tempName );
    
    // 項目使用的是GBK編碼,須要作轉換
    $username = iconv('gbk', 'utf-8', getUserNameById($personData[0]['user_id']) );
    $personal_type = $personData[0]['personal_type'];
    
    // 模板替換開始
    // 能夠輸出打勾的方框
    $deptA=$deptBP=$deptB=$deptC=$deptD = '□';
    if( $DirectorLevel == 'A' ) {
        $deptA = '☑';
    } elseif( $DirectorLevel == 'B+' ) {
        $deptBP = '☑';
    } elseif( $DirectorLevel == 'B' ) {
        $deptB = '☑';
    } elseif( $DirectorLevel == 'C' ) {
        $deptC = '☑';
    } elseif( $DirectorLevel == 'D' ) {
        $deptD = '☑';
    }

    $word->setValue('deptA', $deptA);
    $word->setValue('deptBP', $deptBP);
    $word->setValue('deptB', $deptB);
    $word->setValue('deptC', $deptC);
    $word->setValue('deptD', $deptD);

    // 設置其它替換
    // ...
    
    // 生成臨時文件以供下載
    $tmpFileName = md5( time().'Heier' );
    
    $word->save($personBasePath . '/' . $tmpFileName .'.docx');
    $file = $personBasePath . '/' . $tmpFileName .'.docx';
    
    // 下載Word文件
    ob_start(); //打開緩衝區
    $fp = fopen($file,"r");
    $file_size = filesize($file);
    $downFileName = 'XXX.docx';
    
    header("Cache-Control: public");
    header("Content-type: application/octet-stream");
    header("Accept-Ranges: bytes");
    header("Content-Disposition: attachment; filename={$downFileName}");
    header("Pragma:no-cache");
    header("Expires:0");
    $buffer = 1024;
    $file_count = 0;
    //向瀏覽輸出回數據
    while(!feof($fp) && $file_count < $file_size){
        $file_con = fread($fp,$buffer);
        $file_count += $buffer;
        echo $file_con;
    }
    ob_end_flush();//輸出所有內容到瀏覽器
}

參考文檔彙總

  1. CodePlex: PHPWord;

  2. Github: PHPOffice/PHPWord;

  3. phpword: setValue() not working;

  4. stackoverflow: PhpWord doesn't replace text;

  5. Github: Arisse/PHPWord_CloneRow;

  6. CSDN: PHPWord利用模板替換字符串生成精確的word文檔;

  7. CSDN: PhpWord 如何輸出換行符到 word?

關於我

文章轉載自個人博客:
Heier Blog: 使用PHPWord對Word文件作模板替換

相關文章
相關標籤/搜索