} */ public static function separateStemAndOptions(string $content): array { $contentWithoutSvg = preg_replace('/]*>.*?<\/svg>/is', '[SVG_PLACEHOLDER]', $content); $hasOptions = preg_match('/(?:^|\s)[A-D][\.、:.:]/u', $contentWithoutSvg); if (! $hasOptions) { return [$content, []]; } $options = self::extractOptions($content); if (! empty($options)) { if (preg_match('/^(.+?)(?=(?:^|\s)[A-D][\.、:.:])/su', $contentWithoutSvg, $match)) { $stem = trim($match[1]); if (strpos($stem, '[SVG_PLACEHOLDER]') !== false) { foreach (['A.', 'A、', 'A:', 'A.', 'A:'] as $marker) { if (preg_match('/\s'.preg_quote($marker, '/').'/', $content, $m, PREG_OFFSET_CAPTURE)) { $pos = $m[0][1]; if ($pos > 0) { $stem = trim(mb_substr($content, 0, $pos)); break; } } } } } else { $stem = $content; foreach (['A.', 'A、', 'A:', 'A.', 'A:'] as $marker) { if (preg_match('/\s'.preg_quote($marker, '/').'/', $content, $m, PREG_OFFSET_CAPTURE)) { $pos = $m[0][1]; if ($pos > 0) { $stem = trim(mb_substr($content, 0, $pos)); break; } } } } $stem = preg_replace('/()\s*$/', '', $stem); $stem = trim($stem); return [$stem, $options]; } return [$content, []]; } /** * 与 ExamPdfController::extractOptions 一致 * * @return array */ public static function extractOptions(string $content): array { $options = []; $contentWithoutSvg = preg_replace('/]*>.*?<\/svg>/is', '[SVG_PLACEHOLDER]', $content); $pattern = '/(?:^|\s)([A-D])[\.、:.:]\s*(.+?)(?=(?:^|\s)[A-D][\.、:.:]|$)/su'; if (preg_match_all($pattern, $contentWithoutSvg, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $optionText = trim($match[2]); $optionText = preg_replace('/\s+$/', '', $optionText); $optionText = preg_replace('/^\$\$\s*/', '', $optionText); $optionText = preg_replace('/\s*\$\$$/', '', $optionText); if (! empty($optionText)) { $options[] = $optionText; } } } if (empty($options)) { $lines = preg_split('/[\r\n]+/', $contentWithoutSvg); foreach ($lines as $line) { $line = trim($line); if (preg_match('/^([A-D])[\.、:.:]\s*(.+)$/u', $line, $match)) { $optionText = trim($match[2]); if (! empty($optionText)) { $options[] = $optionText; } } } } return $options; } /** * 与 paper-body 选择题中 $renderedStem 逻辑一致 */ private static function applyBlankPlaceholdersLikeGrading(string $stemLine): string { $blankSpan = ' '; $renderedStem = $stemLine; $renderedStem = preg_replace('/\\\underline\{[^}]*\}/', $blankSpan, $renderedStem); $renderedStem = preg_replace('/\\\qquad+/', $blankSpan, $renderedStem); $latexPlaceholders = []; $counter = 0; $renderedStem = preg_replace_callback('/\$[^$]+\$/u', function ($matches) use (&$latexPlaceholders, &$counter) { $placeholder = '<<>>'; $latexPlaceholders[$placeholder] = $matches[0]; $counter++; return $placeholder; }, $renderedStem); $renderedStem = preg_replace(['/(\s*)/u', '/\(\s*\)/', '/_{2,}/'], $blankSpan, $renderedStem); foreach ($latexPlaceholders as $placeholder => $latexContent) { $encodedLatex = htmlspecialchars($latexContent, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $renderedStem = str_replace($placeholder, $encodedLatex, $renderedStem); } if ($renderedStem === $stemLine) { $renderedStem .= ' '.$blankSpan; } return $renderedStem; } }