使用PHPWord合併Word文檔,在文檔指定頁插入另外一個文檔的內容

提示:不支持.doc文件的讀取
有一個客戶有這樣的需求,須要在ThinkPHP裏使用PHPWord組件,把一個文檔(DOC1)的內容,插入另外一個文檔(DOC2)的指定頁內。因爲兩個文檔的內容都不是固定的,因此不能使用PHPWord的Template功能。
之前歷來沒有使用過PHPWord,因此先後也折騰了幾天。從熟悉PHPWord的功能開始,看示例代碼,還有源碼,最終仍是摸索出來解決方案,下面說下解決思路:php

首先讀取DOC1的內容,PHPWord把內容分紅不一樣的節點和容器,最頂級的是Section,裏面有TextRun(文本塊),Table(表格)等容器。這些容器裏又有Text(文本),Row(表格行),Cell(單元格)等,還有一些其餘的TextBreak,PageBreak(分頁符)等製表符。
逐級讀取內容後,而後把讀取出的內容插入到一個新的文檔內。當讀取到指定的分頁符以後,再讀取DOC2的內容,跟着前面的內容插入,最後保存新的文檔。程序員

貼一部分代碼:this

namespace Home\Logic;

Vendor('PhpOffice.autoload');

use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Shared\ZipArchive;
use PhpOffice\PhpWord\Settings;

class MergeFile {
    private $currentPage = 0;  // 當前分頁
    private $page = 0; // 插入頁數
    private $args = null; // 文本段樣式
    private $tmpFiles = []; // 臨時文件
    
    /**
     * 合併文件
     * 
     * @param URI
     *    文件1地址
     * @param URI
     *    文件2地址
     * @param Numeric
     *    指定插入的頁數
     *
     * @return String
     *    新文件的URI
     */
    public function joinFile($file1, $file2, $page)
    {
        $S1 = IOFactory::load($file1)->getSections();
        $S2 = IOFactory::load($file2)->getSections();
        $this->page = $page > 0 ? $page - 1 : $page;
        
        $phpWord = new PhpWord();

        foreach ($S1 as $S) {
            
            $section = $phpWord->addSection($S->getStyle());

            $elements = $S->getElements();
            # 逐級讀取/寫入節點
            $this->copyElement($elements, $section, $S2);
        }

        $F1 = IOFactory::createWriter($phpWord);
        $path = $_SERVER['DOCUMENT_ROOT'] . __ROOT__ . '/Public/Write/';
        if(!is_dir($path)) mkdir($path);
        $filePath = $path . time() . '.docx';
        $F1->save($filePath);
        # 清除臨時文件
        foreach($this->tmpFiles as $P) {
            unlink($P);
        }
        return $filePath;
    }

    /**
     * 逐級讀取/寫入節點
     *
     * @param Array
     *    須要讀取的節點
     * @param PhpOffice\PhpWord\Element\Section
     *    節點的容器
     * @param Array
     *    文檔2的全部節點
     */
    private function copyElement($elements, &$container, $S2 = null)
    {
        $inEls = [];
        foreach ($elements as $i => $E) {
            # 檢查當前頁數
            if ($this->currentPage == $this->page && !is_null($S2)) {
                # 開始插入
                foreach ($S2 as $k => $v) {
                    $ELS = $v->getElements();
                    $this->copyElement($ELS, $container);
                }
                # 清空防止重複插入
                $S2 = null;
            }
            
            $ns = get_class($E);
            $elName = end(explode('\\', $ns));
            $fun = 'add' . $elName;

            # 統計頁數
            if ($elName == 'PageBreak') {
                $this->currentPage ++;
            }

            # 合併文本段
            if($elName == 'TextRun' 
                #&& !is_null($S2)
                ) {
                $tmpEls = $this->getTextElement($E);
                if(!is_null($tmpEls)) {
                    $inEls = array_merge($inEls, $tmpEls);
                }

                $nextElName = '';
                
                if($i + 1 < count($elements)) {
                    $nextE = $elements[$i + 1];
                    $nextClass = get_class($nextE);
                    $nextElName = end(explode('\\', $nextClass));
                }

                if($nextElName == 'TextRun') {
                    # 對比當前和下一個節點的樣式
                    if(is_object(end($inEls))) {
                        $currentStyle = end($inEls)->getFontStyle();
                    } else {
                        continue;
                    }
                    
                    $nextEls = $this->getTextElement($nextE);
                    if(is_null($nextEls)) {
                        $nextStyle = new Font();
                    } else {
                        $nextStyle = current($nextEls)->getFontStyle();
                    }
                    
                }
            }

            # 設置參數
            $a = $b = $c = $d = $e = null;
            @list($a, $b, $c, $d, $e) = $args;
            $newEl = $container->$fun($a, $b, $c, $d, $e);
            $this->setAttr($elName, $newEl, $E);
            
            #$inEls = [];
            if(method_exists($E, 'getElements') 
                && $elName != 'TextRun'
                ) {
                $inEls = $E->getElements();
            }
            if(method_exists($E, 'getRows'))
                $inEls = $E->getRows();
            if(method_exists($E, 'getCells'))
                $inEls = $E->getCells();
            
            if (count($inEls) > 0) {
                $this->copyElement($inEls, $newEl);
                $inEls = [];
                $this->args = null;
            }
                
        }

        return $pageIndex;
    }

    /**
     * 獲取Text節點
     */
    private function getTextElement($E) {
        $elements = $E->getElements();
        $result = [];
        foreach($elements as $inE) {
            $ns = get_class($inE);
            $elName = end(explode('\\', $ns));
            if($elName == 'Text') {
                $inE->setPhpWord(null);
                $result[] = $inE;
                    
            } elseif (method_exists($inE, 'getElements')) {
                $inResult = $this->getTextElement($inE);
            }
            if(!is_null($inResult))
                $result = array_merge($result, $inResult);
        }
        return count($result) > 0 ? $result : null;
    }

    private function setAttr($elName, &$newEl, $E)
    {
        switch (strtolower($elName)) {
            case 'footnote':
                $newEl->setReferenceId($E->getReferenceId());
                break;
            case 'formfield':
                $newEl->setName($E->getName());
                $newEl->setDefault($E->getDefault());
                $newEl->setValue($E->getValue());
                $newEl->setEntries($E->getEntries());
                break;
            case 'object':
                $newEl->setImageRelationId($E->getImageRelationId());
                $newEl->setObjectId($E->getObjectId());
                break;
            case 'sdt':
                $newEl->setValue($E->getValue());
                $newEl->setListItems($E->getListItems());
                break;
            case 'table':
                $newEl->setWidth($E->getWidth());
                break;
        }
    }

}

程序員客棧,聚集各路碼農,找到你的靠譜技術小夥伴 http://t.cn/RXz4ONTspa

相關文章
相關標籤/搜索