You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 07:20:26