فهرست منبع

卷子生成pdf效果制作

yemeishu 2 هفته پیش
والد
کامیت
c27a4d4348
1فایلهای تغییر یافته به همراه72 افزوده شده و 11 حذف شده
  1. 72 11
      app/Services/ExamPdfExportService.php

+ 72 - 11
app/Services/ExamPdfExportService.php

@@ -87,20 +87,34 @@ class ExamPdfExportService
 
     private function buildPdf(string $html): ?string
     {
-        // 使用无头 Chrome 渲染 HTML,保留前端样式并彻底解决大量空白页问题
         $tmpHtml = tempnam(sys_get_temp_dir(), 'exam_html_') . '.html';
-        $tmpPdf = tempnam(sys_get_temp_dir(), 'exam_pdf_') . '.pdf';
-
         file_put_contents($tmpHtml, $this->ensureUtf8Html($html));
 
+        // 先尝试 Chrome
+        $chromePdf = $this->renderWithChrome($tmpHtml);
+        if ($chromePdf !== null) {
+            @unlink($tmpHtml);
+            return $chromePdf;
+        }
+
+        // Chrome 失败则降级 wkhtmltopdf,尽量保证有输出
+        $wkPdf = $this->renderWithWkhtml($tmpHtml);
+        @unlink($tmpHtml);
+        return $wkPdf;
+    }
+
+    private function renderWithChrome(string $htmlPath): ?string
+    {
+        $tmpPdf = tempnam(sys_get_temp_dir(), 'exam_pdf_') . '.pdf';
+
         $chromeBinary = env('PDF_CHROME_BINARY');
         if (!$chromeBinary) {
-            // 默认优先 Mac,本地开发;不存在则尝试常见 Linux 路径
             $candidates = [
                 '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
+                '/usr/bin/google-chrome-stable',
+                '/usr/bin/google-chrome',
                 '/usr/bin/chromium-browser',
                 '/usr/bin/chromium',
-                '/usr/bin/google-chrome',
             ];
             foreach ($candidates as $path) {
                 if (is_file($path) && is_executable($path)) {
@@ -111,16 +125,17 @@ class ExamPdfExportService
         }
 
         if (!$chromeBinary) {
-            Log::error('ExamPdfExportService: 未找到可用的 Chrome/Chromium 可执行文件');
+            Log::warning('ExamPdfExportService: 未找到可用的 Chrome/Chromium,可尝试 wkhtmltopdf');
             return null;
         }
+
         $process = new Process([
             $chromeBinary,
             '--headless',
             '--disable-gpu',
             '--no-sandbox',
             '--disable-setuid-sandbox',
-            '--disable-dev-shm-usage', // 避免容器内 /dev/shm 过小导致崩溃
+            '--disable-dev-shm-usage',
             '--no-zygote',
             '--single-process',
             '--disable-features=VizDisplayCompositor',
@@ -128,9 +143,9 @@ class ExamPdfExportService
             '--print-to-pdf=' . $tmpPdf,
             '--print-to-pdf-no-header',
             '--allow-file-access-from-files',
-            'file://' . $tmpHtml,
+            'file://' . $htmlPath,
         ]);
-        $process->setTimeout(30);
+        $process->setTimeout(40);
         $process->run();
 
         if (!$process->isSuccessful() || !file_exists($tmpPdf)) {
@@ -140,15 +155,61 @@ class ExamPdfExportService
                 'error' => $process->getErrorOutput(),
                 'output' => $process->getOutput(),
             ]);
-            @unlink($tmpHtml);
             @unlink($tmpPdf);
             return null;
         }
 
         $pdfBinary = file_get_contents($tmpPdf);
-        @unlink($tmpHtml);
         @unlink($tmpPdf);
+        return $pdfBinary ?: null;
+    }
+
+    private function renderWithWkhtml(string $htmlPath): ?string
+    {
+        $tmpPdf = tempnam(sys_get_temp_dir(), 'exam_pdf_wk_') . '.pdf';
+
+        $wkBinary = env('PDF_WKHTML_BINARY');
+        if (!$wkBinary) {
+            $candidates = [
+                '/usr/bin/wkhtmltopdf',
+                '/usr/local/bin/wkhtmltopdf',
+            ];
+            foreach ($candidates as $path) {
+                if (is_file($path) && is_executable($path)) {
+                    $wkBinary = $path;
+                    break;
+                }
+            }
+        }
+
+        if (!$wkBinary) {
+            Log::error('ExamPdfExportService: 未找到可用的 Chrome,且 wkhtmltopdf 未安装,无法导出');
+            return null;
+        }
+
+        $process = new Process([
+            $wkBinary,
+            '--disable-smart-shrinking',
+            '--encoding', 'utf-8',
+            $htmlPath,
+            $tmpPdf,
+        ]);
+        $process->setTimeout(40);
+        $process->run();
 
+        if (!$process->isSuccessful() || !file_exists($tmpPdf)) {
+            Log::error('ExamPdfExportService: wkhtmltopdf 渲染失败', [
+                'cmd' => implode(' ', (array) $process->getCommandLine()),
+                'exit_code' => $process->getExitCode(),
+                'error' => $process->getErrorOutput(),
+                'output' => $process->getOutput(),
+            ]);
+            @unlink($tmpPdf);
+            return null;
+        }
+
+        $pdfBinary = file_get_contents($tmpPdf);
+        @unlink($tmpPdf);
         return $pdfBinary ?: null;
     }