|
|
@@ -1704,11 +1704,11 @@ class ExamPdfExportService
|
|
|
'has_katex_local' => $hasKatexLocal,
|
|
|
]);
|
|
|
|
|
|
- // 如果既没有 CDN 也没有本地链接,跳过
|
|
|
+ // 如果既没有 CDN 也没有本地链接,仍尝试注入 KaTeX 关系符通用修复
|
|
|
if (! $hasKatexCdn && ! $hasKatexLocal) {
|
|
|
Log::warning('ExamPdfExportService: HTML 中没有 KaTeX 资源链接,跳过内联');
|
|
|
|
|
|
- return $html;
|
|
|
+ return $this->applyKatexRelationGlyphFixes($html);
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
@@ -1808,6 +1808,10 @@ class ExamPdfExportService
|
|
|
'after_length' => $afterLength,
|
|
|
'size_change' => $afterLength - $beforeLength,
|
|
|
]);
|
|
|
+
|
|
|
+ // 通用修复:统一优化 KaTeX 关系运算符(mrel)字形
|
|
|
+ // 覆盖平行、垂直、等于、不等于等关系符,避免出现“平行符像 ||”的问题
|
|
|
+ $html = $this->applyKatexRelationGlyphFixes($html);
|
|
|
} else {
|
|
|
Log::warning('ExamPdfExportService: KatexRenderer 未初始化,跳过预渲染');
|
|
|
}
|
|
|
@@ -1823,7 +1827,95 @@ class ExamPdfExportService
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
- return $html;
|
|
|
+ return $this->applyKatexRelationGlyphFixes($html);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 应用 KaTeX 关系符通用修复(先标记,再注入样式)
|
|
|
+ */
|
|
|
+ private function applyKatexRelationGlyphFixes(string $html): string
|
|
|
+ {
|
|
|
+ $html = $this->tagKatexParallelRelationSpans($html);
|
|
|
+
|
|
|
+ return $this->injectKatexRelationGlyphStyle($html);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记 KaTeX 中的平行关系符(∥),便于使用字体无关样式兜底
|
|
|
+ */
|
|
|
+ private function tagKatexParallelRelationSpans(string $html): string
|
|
|
+ {
|
|
|
+ if (strpos($html, '∥') === false || strpos($html, 'mrel') === false) {
|
|
|
+ return $html;
|
|
|
+ }
|
|
|
+
|
|
|
+ $pattern = '/<span(?P<attrs>[^>]*)class=(["\'])(?P<class>[^"\']*\bmrel\b[^"\']*)\2(?P<tail>[^>]*)>\s*∥\s*<\/span>/u';
|
|
|
+ $replaced = preg_replace_callback($pattern, static function (array $matches): string {
|
|
|
+ $attrs = $matches['attrs'] ?? '';
|
|
|
+ $quote = $matches[2] ?? '"';
|
|
|
+ $class = trim($matches['class'] ?? '');
|
|
|
+ $tail = $matches['tail'] ?? '';
|
|
|
+
|
|
|
+ if ($class === '') {
|
|
|
+ $class = 'mrel katex-rel-parallel';
|
|
|
+ } elseif (! preg_match('/\bkatex-rel-parallel\b/', $class)) {
|
|
|
+ $class .= ' katex-rel-parallel';
|
|
|
+ }
|
|
|
+
|
|
|
+ return '<span'.$attrs.'class='.$quote.$class.$quote.$tail.'>∥</span>';
|
|
|
+ }, $html);
|
|
|
+
|
|
|
+ return $replaced ?? $html;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注入 KaTeX 关系运算符统一字形样式(全局、通用)
|
|
|
+ */
|
|
|
+ private function injectKatexRelationGlyphStyle(string $html): string
|
|
|
+ {
|
|
|
+ if (strpos($html, 'class="katex"') === false && strpos($html, 'class="katex-html"') === false) {
|
|
|
+ return $html;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strpos($html, 'id="katex-relation-glyph-style"') !== false) {
|
|
|
+ return $html;
|
|
|
+ }
|
|
|
+
|
|
|
+ $style = '<style id="katex-relation-glyph-style">'
|
|
|
+ .'.katex .mrel{'
|
|
|
+ .'font-family:"KaTeX_AMS","KaTeX_Main","NotoSerifCJKsc-Regular","NotoSerifCJKsc-Bold","NotoSans-Regular","NotoSans-Bold","NotoSansMonoCJKjp-Regular",serif !important;'
|
|
|
+ .'letter-spacing:.04em;'
|
|
|
+ .'}'
|
|
|
+ .'.katex .mrel .mord{font-family:inherit !important;}'
|
|
|
+ .'.katex .mrel.katex-rel-parallel{'
|
|
|
+ .'display:inline-block;'
|
|
|
+ .'position:relative;'
|
|
|
+ .'min-width:.68em;'
|
|
|
+ .'height:.92em;'
|
|
|
+ .'line-height:.92em;'
|
|
|
+ .'font-size:0 !important;'
|
|
|
+ .'vertical-align:-.06em;'
|
|
|
+ .'}'
|
|
|
+ .'.katex .mrel.katex-rel-parallel::before,.katex .mrel.katex-rel-parallel::after{'
|
|
|
+ .'content:"";'
|
|
|
+ .'position:absolute;'
|
|
|
+ .'top:.06em;'
|
|
|
+ .'bottom:.06em;'
|
|
|
+ .'width:.08em;'
|
|
|
+ .'border-radius:.02em;'
|
|
|
+ .'background:currentColor;'
|
|
|
+ .'transform:skewX(-18deg);'
|
|
|
+ .'transform-origin:center;'
|
|
|
+ .'}'
|
|
|
+ .'.katex .mrel.katex-rel-parallel::before{left:.22em;}'
|
|
|
+ .'.katex .mrel.katex-rel-parallel::after{left:.42em;}'
|
|
|
+ .'</style>';
|
|
|
+
|
|
|
+ if (stripos($html, '</head>') !== false) {
|
|
|
+ return preg_replace('/<\/head>/i', $style.'</head>', $html, 1) ?? ($html.$style);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $html.$style;
|
|
|
}
|
|
|
|
|
|
/**
|