Skip to content

Commit fe4b9dc

Browse files
committed
fix: render mixed Korean+LaTeX in PDF with server-side KaTeX
- Add renderMixedLatex() to parse $...$ and $$...$$ delimiters - Render only math segments via KaTeX, escape plain text separately - Add window.print() auto-trigger for PDF save dialog
1 parent df6e934 commit fe4b9dc

1 file changed

Lines changed: 35 additions & 8 deletions

File tree

apps/web/src/server/services/pdf.service.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ ${itemsHtml}
202202
<span class="page-info"></span>
203203
</footer>
204204
205+
<script>window.addEventListener("load",function(){window.print()});</script>
205206
</body>
206207
</html>`;
207208
}
@@ -217,14 +218,10 @@ function buildItemHtml(
217218
): string {
218219
const { item, points } = assignmentItem;
219220

220-
// bodyHtml 우선 사용, 없으면 서버사이드 KaTeX 렌더링
221-
let content: string;
222-
if (item.bodyHtml) {
223-
content = item.bodyHtml;
224-
} else {
225-
const { html } = renderLatex(item.bodyLatex, { displayMode: true });
226-
content = html;
227-
}
221+
// bodyHtml 우선 사용, 없으면 $...$ 구간만 서버사이드 KaTeX 렌더링
222+
const content = item.bodyHtml
223+
? item.bodyHtml
224+
: renderMixedLatex(item.bodyLatex);
228225

229226
const pointsBadge =
230227
points != null
@@ -346,6 +343,36 @@ function buildPrintStyles(): string {
346343
</style>`;
347344
}
348345

346+
/**
347+
* 한국어+LaTeX 혼합 텍스트에서 $...$ 구간만 KaTeX로 렌더링한다.
348+
* 예: "$(-5) \\times (-4)$의 값을 구하시오." → <렌더링된 수식>의 값을 구하시오.
349+
*/
350+
function renderMixedLatex(text: string): string {
351+
const MATH_REGEX = /\$\$([\s\S]+?)\$\$|\$([^$]+?)\$/g;
352+
const parts: string[] = [];
353+
let lastIndex = 0;
354+
let match: RegExpExecArray | null;
355+
356+
while ((match = MATH_REGEX.exec(text)) !== null) {
357+
// $..$ 앞의 일반 텍스트는 이스케이프
358+
if (match.index > lastIndex) {
359+
parts.push(escapeHtml(text.slice(lastIndex, match.index)));
360+
}
361+
const latex = match[1] ?? match[2] ?? "";
362+
const isDisplay = match[1] != null;
363+
const { html } = renderLatex(latex, { displayMode: isDisplay });
364+
parts.push(html);
365+
lastIndex = match.index + match[0].length;
366+
}
367+
368+
// 남은 텍스트
369+
if (lastIndex < text.length) {
370+
parts.push(escapeHtml(text.slice(lastIndex)));
371+
}
372+
373+
return parts.join("");
374+
}
375+
349376
/** HTML 특수 문자를 이스케이프한다. */
350377
function escapeHtml(text: string): string {
351378
const MAP: Readonly<Record<string, string>> = {

0 commit comments

Comments
 (0)