PHPWord的應用

網路與網站相關議題和知識
回覆文章
dtchang
Site Admin
文章: 114
註冊時間: 2017-01-22, 16:54

PHPWord的應用

文章 dtchang » 2024-12-30, 22:58

1. 安裝 composer (前提 PHP需安裝有 SSL和 mbstring的extension功能)
2. 安裝 PHPWord (依PHP版本不同,會有版本上限)(PHP 5.6 只能用到 0.17.0)

3. 簡易入手文件,可參考 https://gist.github.com/thomasjao/316fb835dd1e3a3333aa
4. 好好利用 ChatGPT, 時代不同了. 雖然ChatGPT未必能給出 100% 正確的回答.

可在 c:\composer 目錄下建立 PHPWord子目錄, 然後開啟 command 模式, cd C:\composer\PHPWord, 再下達 composer 指令:

代碼: 選擇全部

composer require phpoffice/phpword:^0.17.0
最新版則為

代碼: 選擇全部

composer require phpoffice/phpword
3. 輸出Word的重點

代碼: 選擇全部

require_once 'vendor/autoload.php';
header: header('Content-Type: text/html; charset=utf-8');

use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\IOFactory;
PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true); //可避免輸出 <= 這類的問題

// 初始化 PHPWord
$phpWord = new PhpWord();
...
// 儲存 Word 文件
$fileName = 'output.docx';
$writer = IOFactory::createWriter($phpWord, 'Word2007');
ob_clean(); //重要,否則可能會產生: This file is corrupt and cannot be opened. 的開檔警告訊息
$writer->save($fileName);

// 提供下載
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=" . basename($fileName));
header("Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    header("Content-Transfer-Encoding: binary");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Pragma: public");
    header("Content-Length: " . filesize($fileName));
readfile($fileName);

// 刪除文件
unlink($fileName);
exit;
5. 產生的WORD檔無法開啟時,可以將下載的WORD檔重新命名為 .zip, 將其解壓縮後檢查 word/document.xml 的格式是否有錯?
https://www.runoob.com/xml/xml-validator.html
大都會產生的錯誤原因為資料裡有 < >; 或者資料裡含 html tags, 此時若丟給 addText()會產生錯誤(0.17.0版會有錯誤).

代碼: 選擇全部

// 匹配完整的 <table> 和 <img> 標籤
$patternTable = '/<\s*table\b[^>]*>/i';
$patternImg = '/<\s*img\b[^>]*>/i';

$search = array('<br/>','<br />', '&nbsp;','<p>','<p/>','</p>',
'<h1>','</h1>','<h2>','</h2>','<h3>','</h3>');
$replace= array("\n","\n",' ',"","\n","\n","[","]","[","]","[","]");
...
	       if (preg_match($patternTable, $htmlContent) || 
		       preg_match($patternImg, $htmlContent)) {
    		processHtmlContent($section, $htmlContent);
		   }
		   else {
			   $htmlContent = str_ireplace($search, $replace, $htmlContent); //替換
			   $section->addText($htmlContent, array('name' => 'Arial', 'size' => 12)); 
		   }

因為轉成 Word檔時,時常要處理的是表格和圖檔,故確認有圖或表格時,調用特化的函式.
否則就用一般的方式處理.

代碼: 選擇全部


// 定義處理圖片的函式
function processImage($sectionOrTextRun, $imgNode, $isTextRun = false) {
    $imgSrc = $_SESSION["PICT_ROOT"].$imgNode->getAttribute('src');
    // 獲取圖片原始大小
    list($originalWidth, $originalHeight) = getimagesize($imgSrc);
	
    // 縮小圖片為原始大小的 1/2
    $scaledWidth = intval($originalWidth / 2);
    $scaledHeight = intval($originalHeight / 2);

    if (file_exists($imgSrc)) {
        $options = array(
            'width' => $scaledWidth,
            'height' => $scaledHeight,
        );

        if ($isTextRun) {
            $sectionOrTextRun->addImage($imgSrc, $options);
        } else {
            $sectionOrTextRun->addImage($imgSrc, array_merge($options, array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER)));
        }
    } else {
        $sectionOrTextRun->addText("圖片未找到: $imgSrc", array('color' => 'FF0000', 'name' => 'Arial', 'size' => 12));
    }
}

// 定義處理表格的函式
function processTable($section, $tableNode) {
    $wordTable = $section->addTable(array(
        'borderSize' => 6,
        'borderColor' => '000000',
        'alignment' => 'center'
    ));
    $rows = $tableNode->getElementsByTagName('tr');
    foreach ($rows as $row) {
        $wordRow = $wordTable->addRow();
        $cells = $row->getElementsByTagName('td');
        foreach ($cells as $cell) {
            $wordRow->addCell(2000)->addText(trim($cell->textContent), array('name' => 'Arial', 'size' => 12));
        }
    }
}

// 定義處理 HTML 的函式
function processHtmlContent($section, $htmlContent) {
    libxml_use_internal_errors(true);
    $dom = new DOMDocument('1.0', 'utf-8');
    @$dom->loadHTML('<?xml encoding="UTF-8">' . $htmlContent);
    libxml_clear_errors();

    $body = $dom->getElementsByTagName('body')->item(0);
    foreach ($body->childNodes as $node) {
        if ($node->nodeName === 'p') {
            $textRun = $section->addTextRun();
            foreach ($node->childNodes as $childNode) {
                if ($childNode->nodeName === '#text') {
                    $textRun->addText(trim($childNode->textContent), array('name' => 'Arial', 'size' => 12));
                } elseif ($childNode->nodeName === 'img') {
                    processImage($textRun, $childNode, true);
                }
            }
        } elseif ($node->nodeName === 'table') {
            processTable($section, $node);
        } elseif ($node->nodeName === 'img') {
            processImage($section, $node);
        }
    }
}


回覆文章