Thinkphp使用pptx模板生成pptx

<?php
use DOMDocument;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;

class PptxXmlModifier
{
    private $tempDir;
    private $namespaceA = 'http://schemas.openxmlformats.org/drawingml/2006/main';

    // 1. 解压 PPTX 文件
    private function unzip($pptxPath)
    {
        //$this->tempDir在$zip->addFile($filePath, $relativePath);使用中;relativePath 必须是 Zip 内部相对路径,使用 正斜杠 /,否则会导致 PPTX 无法打开。
        $uniqid_file_path = ROOT_PATH .  'runtime/cache/uniqid/'.'pptx_' . uniqid();
        $this->tempDir = str_replace('\\', '/',$uniqid_file_path);
        if (!is_dir($this->tempDir)) {
            mkdir($this->tempDir, 0777, true); // 递归创建目录
        }

        $zip = new ZipArchive();
        if ($zip->open($pptxPath) === TRUE) {
            $zip->extractTo($this->tempDir);
            $zip->close();
            return true;
        }
        return false;
    }

    // 2. 替换文本(保留 Run 样式结构)
    private function replaceText($replacements)
    {
        $slidesPath = $this->tempDir . '/ppt/slides';
        $slideFiles = glob($slidesPath . '/slide*.xml');

        foreach ($slideFiles as $slideFile) {
            $dom = new DOMDocument();
            $dom->preserveWhiteSpace = false;
            $dom->formatOutput = true;
            $dom->load($slideFile);

            // 获取所有 <a:t> 文本节点
            $textNodes = $dom->getElementsByTagNameNS($this->namespaceA, 't');

            foreach ($textNodes as $node) {
                $originalText = $node->nodeValue;
                foreach ($replacements as $placeholder => $newValue) {
                    if (strpos($originalText, $placeholder) !== false) {
                        // 核心:只修改节点的值,完全不触碰父级 <a:r> 和 <a:rPr> 中的任何样式属性
                        $node->nodeValue = str_replace($placeholder, $newValue, $originalText);
                    }
                }
            }
            $dom->save($slideFile);
        }
    }

    // 3. 替换图片(支持本地路径和 Base64 字符串)
    private function replaceImage($imageMap)
    {
        // $imageMap 格式: ['old_image_filename.jpg' => '新的图片数据']
        $mediaDir = $this->tempDir . '/ppt/media';

        foreach ($imageMap as $oldName => $newImagepath) {
            $newImageSource = ROOT_PATH . $newImagepath;
            // PPT中的目标图片
            $targetImage = $mediaDir . '/' . $oldName;
            if (file_exists($targetImage)) {
                $data = file_get_contents($newImageSource);
                if ($data) {
                    // 替换图片
                    file_put_contents($targetImage, $data);
                }
            }
        }
    }

    // 4. 重新打包成新的 PPTX
    private function save($outputPath)
    {
        $zip = new ZipArchive();
        if ($zip->open($outputPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
            return false;
        }

        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($this->tempDir),
            RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($files as $file) {
            if (!$file->isDir()) {
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($this->tempDir) + 1);
                $zip->addFile($filePath, $relativePath);
            }
        }
        return $zip->close();
    }

    // 清理临时文件
    private function cleanup()
    {
        if (is_dir($this->tempDir)) {
            $this->rrmdir($this->tempDir);
        }
    }

    private function rrmdir($dir)
    {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." && $object != "..") {
                    if (is_dir($dir . "/" . $object)) $this->rrmdir($dir . "/" . $object);
                    else unlink($dir . "/" . $object);
                }
            }
            rmdir($dir);
        }
    }

    /**
     * @param $template  模板
     * @param $outputPath 输出文件全路径
     * @param $textReplacements  文本更换内容
     * @param $imageReplacements  图片更换内容
     * @return bool
     */
    public function createpptx($template, $outputPath, $textReplacements, $imageReplacements)
    {

        if ($this->unzip($template)) {

            // 3. 执行替换
            $this->replaceText($textReplacements);
            $this->replaceImage($imageReplacements);

            // 4. 保存并输出新文件
            $this->save($outputPath);
            $this->cleanup();

            return true;
        } else {
            return false;
        }
    }
}

使用

 $template = ROOT_PATH . P_STATIC . DS . 'template.pptx';
            $file_path = ROOT_PATH . P_STATIC . DS . 'week' . DS . $day;
            if (!is_dir($file_path)) {
                mkdir($file_path, 0777, true); // 递归创建目录
            }
            $output = $file_path . DS . $shopid . '.pptx';
            $textReplacements = [
                '{{username}}' => $user_data['name'],
                '{{shopname}}' => $shop_data['shop_name']
            ];
            $feilds = ['1'=>['image3.png','image4.png','image5.png'], '2'=>'image6.png', '3'=>'image7.png', '4'=>'image8.png', '5'=>'image9.png', '6'=>'image10.png', '7'=>'image11.png', '8'=>'image12.png', '9'=>'image13.png', '10'=>'image18.png', '11'=>'image15.png', '12'=>'image16.png', '13'=>'image17.png', '14'=>'image20.png', '15'=>'image19.png', '16'=>'image21.png', '17'=>'image22.png', '18'=>'image23.png', '19'=>'image24.png', '20'=>'image29.png', '21'=>'image14.png'];
            $imageReplacements = [];
            foreach ($feilds as $key=>$feild) {
                if (is_array($feild)){
                    $pic1_arr = json_decode($picdata['pic'.$key]);
                    foreach ($pic1_arr as $index => $pic1_item) {
                            $imageReplacements[$feild[$index]] = $pic1_item;
                        }
                }else{
                    $imageReplacements[$feild]=$picdata['pic'.$key];
                }
            }
            $modifier = new PptxXmlModifier();
            $result = $modifier->createpptx($template, $output, $textReplacements, $imageReplacements);

 

posted @ 2026-05-23 08:59  mingruqi  阅读(8)  评论(0)    收藏  举报