|
27 | 27 | use App\Utils\Utils; |
28 | 28 | use Doctrine\Common\Collections\Collection; |
29 | 29 | use Doctrine\ORM\EntityManagerInterface; |
| 30 | +use Ramsey\Uuid\Uuid; |
30 | 31 | use Symfony\Component\DependencyInjection\Attribute\Autowire; |
31 | 32 | use Symfony\Component\Intl\Countries; |
32 | 33 | use Symfony\Component\Intl\Exception\MissingResourceException; |
|
38 | 39 | use Twig\Environment; |
39 | 40 | use Twig\Extension\AbstractExtension; |
40 | 41 | use Twig\Extension\GlobalsInterface; |
| 42 | +use Twig\Extra\Markdown\MarkdownRuntime; |
41 | 43 | use Twig\Runtime\EscaperRuntime; |
42 | 44 | use Twig\TwigFilter; |
43 | 45 | use Twig\TwigFunction; |
44 | 46 |
|
45 | 47 | class TwigExtension extends AbstractExtension implements GlobalsInterface |
46 | 48 | { |
| 49 | + /** |
| 50 | + * @param array<string> $latexFound |
| 51 | + */ |
| 52 | + private array $latexFound; |
| 53 | + |
47 | 54 | /** |
48 | 55 | * @param array<int, bool> $renderedSources |
49 | 56 | */ |
@@ -126,6 +133,7 @@ public function getFilters(): array |
126 | 133 | new TwigFilter('medalType', $this->awards->medalType(...)), |
127 | 134 | new TwigFilter('numTableActions', $this->numTableActions(...)), |
128 | 135 | new TwigFilter('extensionToMime', $this->extensionToMime(...)), |
| 136 | + new TwigFilter('domjudgeMarkdownToHtml', $this->domjudgeMarkdownToHTML(...), ['is_safe' => ['html']]), |
129 | 137 | ]; |
130 | 138 | } |
131 | 139 |
|
@@ -1381,4 +1389,35 @@ public function extensionToMime(string $extension): string |
1381 | 1389 | { |
1382 | 1390 | return DOMJudgeService::EXTENSION_TO_MIMETYPE[$extension]; |
1383 | 1391 | } |
| 1392 | + |
| 1393 | + /** |
| 1394 | + * Extract all LaTeX code from the given string, sanitize the markdown and |
| 1395 | + * inject the original LaTeX code back so MathJax can render it. |
| 1396 | + */ |
| 1397 | + public function domjudgeMarkdownToHTML(string $markdown): string |
| 1398 | + { |
| 1399 | + $latexPlaceholder = Uuid::uuid4()->toString(); |
| 1400 | + while (str_contains($markdown, $latexPlaceholder)) { |
| 1401 | + $latexPlaceholder = Uuid::uuid4()->toString(); |
| 1402 | + } |
| 1403 | + |
| 1404 | + $markdown = preg_replace_callback( |
| 1405 | + '/(\$[\s\S]*?\$)/', |
| 1406 | + function (array $matches) use ($latexPlaceholder) { |
| 1407 | + // Store and replace matches |
| 1408 | + $this->latexFound[] = $matches[1]; |
| 1409 | + return $latexPlaceholder; |
| 1410 | + }, |
| 1411 | + $markdown |
| 1412 | + ); |
| 1413 | + |
| 1414 | + /** @var MarkdownRuntime $runtime */ |
| 1415 | + $runtime = $this->twig->getRuntime(MarkdownRuntime::class); |
| 1416 | + $markdown = (string)$runtime->convert($markdown); |
| 1417 | + |
| 1418 | + return preg_replace_callback( |
| 1419 | + '/'.$latexPlaceholder.'/', |
| 1420 | + fn() => array_shift($this->latexFound), $markdown |
| 1421 | + ); |
| 1422 | + } |
1384 | 1423 | } |
0 commit comments