瀏覽代碼

fix(pdf): fill stem terminal punctuation append only when missing

yemeishu 3 周之前
父節點
當前提交
ddc25f4702
共有 2 個文件被更改,包括 45 次插入9 次删除
  1. 44 9
      app/Support/BlankPlaceholderRenderer.php
  2. 1 0
      resources/views/components/exam/paper-body.blade.php

+ 44 - 9
app/Support/BlankPlaceholderRenderer.php

@@ -5,6 +5,8 @@ namespace App\Support;
 class BlankPlaceholderRenderer
 {
     private const DEFAULT_BLANK_SPAN = '<span style="display:inline-block; min-width:80px; border-bottom:1.2px dashed #444; vertical-align:bottom;">&nbsp;</span>';
+    // 仅匹配“空白占位”型 underline,不匹配 \underline{\frac{...}} 这类有内容公式下划线
+    private const BLANK_UNDERLINE_PATTERN = '/\\\\+underline\{\s*(?:(?:\\\\+qquad+|\\\\+quad+|\\\\+hspace\{[^{}]*\}|\\\\+hphantom\{\s*(?:(?:\\\\+qquad+|\\\\+quad+|\\\\+hspace\{[^{}]*\}|_{2,}|&nbsp;|&#160;|\s| |\\\\+\s+)*)\s*\}|_{2,}|&nbsp;|&#160;|\s| |\\\\+\s+)*)\s*\}/u';
 
     /**
      * 将题干中的空括号/下划线/部分异常占位符统一替换为标准空位样式。
@@ -31,9 +33,9 @@ class BlankPlaceholderRenderer
             $blankToken = '<<<BLANK_IN_MATH_'.$counter.'>>>';
             $innerWithBlanks = preg_replace(
                 [
-                    '/\\\\underline\{[^}]*\}/u',
-                    '/\\\\qquad+/u',
-                    '/\\\\quad+/u',
+                    self::BLANK_UNDERLINE_PATTERN,
+                    '/\\\\+qquad+/u',
+                    '/\\\\+quad+/u',
                     '/[((](?:\s|&nbsp;|&#160;| )*[))]/u',
                     '/_{2,}/u',
                 ],
@@ -72,8 +74,8 @@ class BlankPlaceholderRenderer
 
         // 兼容常见空位写法:\underline{...}、\qquad、空括号(含 nbsp 等空白)、连续下划线、尾部 \\$
         $patterns = [
-            '/\\\underline\{[^}]*\}/u',
-            '/\\\qquad+/u',
+            self::BLANK_UNDERLINE_PATTERN,
+            '/\\\\+qquad+/u',
             '/[((](?:\s|&nbsp;|&#160;| )*[))]/u',
             '/_{2,}/u',
             '/\\\\+\$(?=\s*$)/u',
@@ -83,6 +85,14 @@ class BlankPlaceholderRenderer
             $quotedBlankSpan = preg_quote($blankSpan, '/');
             $renderedContent = preg_replace('/(?:'.$quotedBlankSpan.'(?:\s|&nbsp;|&#160;| )*){2,}/u', $blankSpan, $renderedContent);
         }
+        // 兼容脏数据:空位后紧跟孤立 "$" 且位于句尾(如 "...=____$."),移除该孤立 "$"。
+        // 仅作用在“标准空位 + 句尾”场景,不影响正常数学公式分隔符。
+        $quotedBlankSpan = preg_quote($blankSpan, '/');
+        $renderedContent = preg_replace(
+            '/('.$quotedBlankSpan.')\s*\$(?=\s*[\..。]?(?:\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*)$)/u',
+            '$1',
+            $renderedContent
+        ) ?? $renderedContent;
 
         foreach ($latexPlaceholders as $placeholder => $latexContent) {
             if (preg_match('/^\$(.*?)(\\\\+)\$$/u', $latexContent, $match)) {
@@ -134,11 +144,36 @@ class BlankPlaceholderRenderer
             return $content;
         }
 
-        // 先处理数学片段尾点(如 "$.$" / "$。$" / "$.$")。
-        $content = preg_replace('/\$\s*[\..。]\s*\$(?=\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*$)/u', $replacement, $content);
+        // 仅处理句尾最后一个标点(允许句尾带 HTML 标签,如 <image .../>)。
+        // 1) 先处理数学片段尾点(如 "$.$" / "$。$" / "$.$")。
+        if (preg_match('/^(.*)\$\s*[\..。]\s*\$(\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*)$/us', $content, $m)) {
+            return $m[1].$replacement.$m[2];
+        }
+
+        // 2) 再处理普通句尾点(只替换最后一个,不影响中间文本)。
+        if (preg_match('/^(.*?)([\..。])(\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*)$/us', $content, $m)) {
+            return $m[1].$replacement.$m[3];
+        }
+
+        return $content;
+    }
+
+    /**
+     * 仅当句尾不存在句号类标点时,追加目标标点。
+     * 不会覆盖已存在的句尾标点,也不处理正文中间内容。
+     */
+    public static function appendTerminalPunctuationIfMissing(string $content, string $punctuation): string
+    {
+        if ($punctuation === '') {
+            return $content;
+        }
+
+        // 句尾若已有终止符号(中英文句号/问号/叹号/分号/冒号),则不再追加
+        if (preg_match('/[\..。!!\??;;::](\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*)$/us', $content)) {
+            return $content;
+        }
 
-        // 再处理普通句尾点。
-        return preg_replace('/[\..。](?=\s*(?:(?:<\/[^>]+>|<[^>]+\/>)\s*)*$)/u', $replacement, $content);
+        return rtrim($content).$punctuation;
     }
 
     private static function normalizeChineseTerminalPeriod(string $content): string

+ 1 - 0
resources/views/components/exam/paper-body.blade.php

@@ -293,6 +293,7 @@
             }
             // 填空题:句尾统一为实心小圆点(英文句点)。
             $renderedContent = \App\Support\BlankPlaceholderRenderer::normalizeTerminalPunctuation($renderedContent, 'dot');
+            $renderedContent = \App\Support\BlankPlaceholderRenderer::appendTerminalPunctuationIfMissing($renderedContent, '.');
             $renderedContent = $mathProcessed ? $renderedContent : \App\Services\MathFormulaProcessor::processFormulas($renderedContent);
         @endphp
         <div class="question">