PHP实现HTML转Word可识别XML以生成DOCX文档
HTML转DOCX的实现方案
完全可以通过生成符合OOXML标准的XML内容,再打包成ZIP格式的docx文件来实现需求,以下是具体的落地步骤:
一、先准备基础的DOCX模板结构
直接从零构建所有必需的XML文件太繁琐,最简单的方式是:
- 创建一个空白的docx文件,将其重命名为
.zip后解压 - 保留解压后的核心目录与文件:
_rels/:存放关系配置word/:核心内容目录,包含document.xml、styles.xml等[Content_Types].xml:文档类型配置
- 把这个解压后的文件夹作为模板,后续只需要替换
word/document.xml的内容即可
二、将HTML转换为Word兼容的OOXML
Word的document.xml遵循OOXML标准,标签逻辑和HTML对应关系明确,针对你提到的标题、段落、表格,转换规则如下:
1. 基础标签映射示例
| HTML标签 | 对应OOXML结构 |
|---|---|
<h1> | <w:p><w:pPr><w:pStyle w:val="Heading1"/></w:pPr><w:r><w:t>标题文本</w:t></w:r></w:p> |
<p> | <w:p><w:r><w:t>段落文本</w:t></w:r></w:p> |
<table> | 用<w:tbl>包裹,<tr>对应<w:tr>,<td>对应<w:tc> |
2. 具体代码片段(PHP)
用DOM解析HTML并生成对应XML:
function htmlToWordXml($htmlContent) { $dom = new DOMDocument(); @$dom->loadHTML('<meta charset="UTF-8">' . $htmlContent); // 避免编码问题 $xmlNs = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'; $xml = "<w:document xmlns:w=\"{$xmlNs}\"><w:body>"; // 遍历HTML节点生成XML foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) { if ($node->nodeType !== XML_ELEMENT_NODE) continue; switch(strtolower($node->tagName)) { case 'h1': case 'h2': case 'h3': $level = substr($node->tagName, 1); $xml .= "<w:p><w:pPr><w:pStyle w:val=\"Heading{$level}\"/></w:pPr>"; $xml .= "<w:r><w:t>" . htmlspecialchars($node->nodeValue) . "</w:t></w:r></w:p>"; break; case 'p': $xml .= "<w:p><w:r><w:t>" . htmlspecialchars($node->nodeValue) . "</w:t></w:r></w:p>"; break; case 'table': $xml .= '<w:tbl><w:tblPr><w:tblW w:w="0" w:type="auto"/></w:tblPr>'; foreach ($node->getElementsByTagName('tr') as $tr) { $xml .= '<w:tr>'; foreach ($tr->getElementsByTagName('td') as $td) { $xml .= '<w:tc><w:p><w:r><w:t>' . htmlspecialchars($td->nodeValue) . '</w:t></w:r></w:p></w:tc>'; } $xml .= '</w:tr>'; } $xml .= '</w:tbl>'; break; } } $xml .= '</w:body></w:document>'; return $xml; }
三、打包生成DOCX文件
用PHP的ZipArchive类把模板目录和生成的XML打包成ZIP,再重命名为docx:
function buildDocx($wordXml, $outputFile) { $templateDir = './docx-template'; // 你的模板目录路径 $tempDir = './temp-docx-' . uniqid(); // 复制模板到临时目录 copyDir($templateDir, $tempDir); // 替换核心XML文件 file_put_contents($tempDir . '/word/document.xml', $wordXml); // 打包成docx $zip = new ZipArchive(); if ($zip->open($outputFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $file) { $relativePath = substr($file->getRealPath(), strlen($tempDir) + 1); $zip->addFile($file->getRealPath(), $relativePath); } $zip->close(); } // 清理临时目录 deleteDir($tempDir); } // 辅助函数:复制目录 function copyDir($source, $dest) { if (!is_dir($dest)) mkdir($dest, 0777, true); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $item) { $item->isDir() ? mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName()) : copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName()); } } // 辅助函数:删除目录 function deleteDir($dir) { if (!is_dir($dir)) return; foreach (scandir($dir) as $file) { if ($file === '.' || $file === '..') continue; is_dir("$dir/$file") ? deleteDir("$dir/$file") : unlink("$dir/$file"); } rmdir($dir); }
四、注意事项
- 特殊字符必须用
htmlspecialchars转义,避免破坏XML结构 - 如果需要自定义样式,修改模板中的
word/styles.xml即可添加自定义段落/字符样式 - 复杂元素(如图片、有序列表)需要额外处理对应的OOXML标签,比如列表要配置
w:numPr,图片要放入word/media并在XML中引用
内容的提问来源于stack exchange,提问作者Daniel Jørgensen
相关产品推荐
相关产品推荐

