提示:不支持.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