|
@@ -24,6 +24,8 @@ use Symfony\Component\Process\Process;
|
|
|
*/
|
|
*/
|
|
|
class ExamPdfExportService
|
|
class ExamPdfExportService
|
|
|
{
|
|
{
|
|
|
|
|
+ private ?KatexRenderer $katexRenderer = null;
|
|
|
|
|
+
|
|
|
public function __construct(
|
|
public function __construct(
|
|
|
private readonly LearningAnalyticsService $learningAnalyticsService,
|
|
private readonly LearningAnalyticsService $learningAnalyticsService,
|
|
|
private readonly QuestionBankService $questionBankService,
|
|
private readonly QuestionBankService $questionBankService,
|
|
@@ -31,7 +33,10 @@ class ExamPdfExportService
|
|
|
private readonly PdfStorageService $pdfStorageService,
|
|
private readonly PdfStorageService $pdfStorageService,
|
|
|
private readonly MasteryCalculator $masteryCalculator,
|
|
private readonly MasteryCalculator $masteryCalculator,
|
|
|
private readonly PdfMerger $pdfMerger
|
|
private readonly PdfMerger $pdfMerger
|
|
|
- ) {}
|
|
|
|
|
|
|
+ ) {
|
|
|
|
|
+ // 延迟初始化 KatexRenderer(避免循环依赖)
|
|
|
|
|
+ $this->katexRenderer = new KatexRenderer();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 生成试卷 PDF(不含答案)
|
|
* 生成试卷 PDF(不含答案)
|
|
@@ -1316,47 +1321,39 @@ class ExamPdfExportService
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 【关键修复】移除原有的DOMContentLoaded异步渲染,改为同步立即渲染
|
|
|
|
|
- // Chrome headless的--print-to-pdf不会等待DOMContentLoaded后的JS执行
|
|
|
|
|
- // 所以我们需要在</body>前添加同步渲染代码
|
|
|
|
|
|
|
+ // 【关键修复】使用服务端预渲染,而不是依赖客户端 JavaScript
|
|
|
|
|
+ // Chrome headless 的 --print-to-pdf 不会等待 JS 执行完成
|
|
|
|
|
+ // 所以我们使用 Node.js KaTeX 在服务端预渲染所有公式
|
|
|
|
|
|
|
|
- // 1. 移除原有的DOMContentLoaded监听器(包括setTimeout延迟)
|
|
|
|
|
|
|
+ // 1. 移除所有 KaTeX JavaScript(不再需要,因为使用服务端渲染)
|
|
|
|
|
+ // 移除内联的 katex.min.js
|
|
|
$html = preg_replace(
|
|
$html = preg_replace(
|
|
|
- '/<script[^>]*>\s*document\.addEventListener\s*\(\s*[\'"]DOMContentLoaded[\'"]\s*,\s*function\s*\(\)\s*\{[\s\S]*?renderMathInElement[\s\S]*?\}\s*\)\s*;?\s*<\/script>/i',
|
|
|
|
|
- '<!-- KaTeX DOMContentLoaded removed, using sync render -->',
|
|
|
|
|
|
|
+ '/<script[^>]*type=["\']text\/javascript["\'][^>]*>[\s\S]*?katex[\s\S]*?<\/script>/i',
|
|
|
|
|
+ '<!-- KaTeX JS removed, using server-side rendering -->',
|
|
|
$html
|
|
$html
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // 2. 在</body>前添加同步渲染脚本
|
|
|
|
|
- $syncRenderScript = '
|
|
|
|
|
-<script type="text/javascript">
|
|
|
|
|
-// 同步执行KaTeX渲染(PDF生成专用)
|
|
|
|
|
-(function() {
|
|
|
|
|
- if (typeof renderMathInElement === "function") {
|
|
|
|
|
- try {
|
|
|
|
|
- renderMathInElement(document.body, {
|
|
|
|
|
- delimiters: [
|
|
|
|
|
- {left: "$$", right: "$$", display: true},
|
|
|
|
|
- {left: "$", right: "$", display: false},
|
|
|
|
|
- {left: "\\\\(", right: "\\\\)", display: false},
|
|
|
|
|
- {left: "\\\\[", right: "\\\\]", display: true}
|
|
|
|
|
- ],
|
|
|
|
|
- throwOnError: false,
|
|
|
|
|
- strict: false,
|
|
|
|
|
- trust: true
|
|
|
|
|
- });
|
|
|
|
|
- console.log("KaTeX sync render completed");
|
|
|
|
|
- } catch(e) {
|
|
|
|
|
- console.error("KaTeX render error:", e);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-})();
|
|
|
|
|
-</script>
|
|
|
|
|
-</body>';
|
|
|
|
|
|
|
+ // 移除 DOMContentLoaded 监听器
|
|
|
|
|
+ $html = preg_replace(
|
|
|
|
|
+ '/<script[^>]*>[\s\S]*?document\.addEventListener[\s\S]*?DOMContentLoaded[\s\S]*?<\/script>/i',
|
|
|
|
|
+ '<!-- DOMContentLoaded removed -->',
|
|
|
|
|
+ $html
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
- $html = str_ireplace('</body>', $syncRenderScript, $html);
|
|
|
|
|
|
|
+ // 2. 使用 KatexRenderer 进行服务端预渲染
|
|
|
|
|
+ if ($this->katexRenderer) {
|
|
|
|
|
+ $beforeLength = strlen($html);
|
|
|
|
|
+ $html = $this->katexRenderer->renderHtml($html);
|
|
|
|
|
+ $afterLength = strlen($html);
|
|
|
|
|
|
|
|
- Log::info('ExamPdfExportService: KaTeX已改为同步渲染模式');
|
|
|
|
|
|
|
+ Log::info('ExamPdfExportService: LaTeX 公式服务端预渲染完成', [
|
|
|
|
|
+ 'before_length' => $beforeLength,
|
|
|
|
|
+ 'after_length' => $afterLength,
|
|
|
|
|
+ 'size_change' => $afterLength - $beforeLength,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Log::warning('ExamPdfExportService: KatexRenderer 未初始化,跳过预渲染');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
Log::warning('ExamPdfExportService: 内联资源处理失败,保留原始HTML', [
|
|
Log::warning('ExamPdfExportService: 内联资源处理失败,保留原始HTML', [
|