using System; using System.Diagnostics; using System.IO; using System.Text; using System.Text.RegularExpressions; using AxCopilot.Models; namespace AxCopilot.Services; public static class PdfExportService { public static string ExportToPrintableHtml(ChatConversation conversation, string outputPath) { string contents = BuildHtml(conversation); File.WriteAllText(outputPath, contents, Encoding.UTF8); return outputPath; } public static string BuildHtml(ChatConversation conversation) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(28, 1, stringBuilder2); handler.AppendLiteral("AX Copilot — "); handler.AppendFormatted(EscapeHtml(conversation.Title)); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); stringBuilder.AppendLine("
"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(EscapeHtml(conversation.Title)); handler.AppendLiteral("

"); stringBuilder4.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(32, 2, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(conversation.CreatedAt, "yyyy-MM-dd HH:mm"); handler.AppendLiteral(" · "); handler.AppendFormatted(conversation.Messages.Count); handler.AppendLiteral("개 메시지
"); stringBuilder5.AppendLine(ref handler); stringBuilder.AppendLine("
"); foreach (ChatMessage message in conversation.Messages) { string value = ((message.Role == "user") ? "user" : "assistant"); string value2 = ((message.Role == "user") ? "사용자" : "AI"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendLiteral("
"); stringBuilder6.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(24, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(value2); handler.AppendLiteral("
"); stringBuilder7.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(27, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(FormatContent(message.Content)); handler.AppendLiteral("
"); stringBuilder8.AppendLine(ref handler); if (message.Timestamp != default(DateTime)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder9 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(24, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(message.Timestamp, "HH:mm"); handler.AppendLiteral("
"); stringBuilder9.AppendLine(ref handler); } stringBuilder.AppendLine("
"); } stringBuilder.AppendLine("
"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder10 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendLiteral("AX Copilot · 내보내기 일시: "); handler.AppendFormatted(DateTime.Now, "yyyy-MM-dd HH:mm"); stringBuilder10.AppendLine(ref handler); stringBuilder.AppendLine("
"); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); return stringBuilder.ToString(); } private static string GetPrintStyles() { return "\n* { box-sizing: border-box; margin: 0; padding: 0; }\nbody { font-family: 'Malgun Gothic', 'Segoe UI', sans-serif; font-size: 13px; color: #222; background: #fff; padding: 20px; }\n.header { border-bottom: 2px solid #4B5EFC; padding-bottom: 12px; margin-bottom: 20px; }\n.header h1 { font-size: 18px; font-weight: 700; color: #1a1b2e; }\n.header .meta { font-size: 11px; color: #888; margin-top: 4px; }\n.message { margin-bottom: 16px; padding: 12px 16px; border-radius: 8px; page-break-inside: avoid; }\n.message.user { background: #f0f4ff; border-left: 3px solid #4B5EFC; }\n.message.assistant { background: #fafafa; border-left: 3px solid #10B981; }\n.role { font-size: 10px; font-weight: 700; color: #888; text-transform: uppercase; margin-bottom: 6px; }\n.content { line-height: 1.7; white-space: pre-wrap; word-break: break-word; }\n.content code { background: #f0f0f5; padding: 1px 4px; border-radius: 3px; font-family: Consolas, monospace; font-size: 12px; }\n.content pre { background: #f5f5fa; padding: 10px; border-radius: 6px; overflow-x: auto; margin: 8px 0; }\n.time { font-size: 10px; color: #aaa; text-align: right; margin-top: 4px; }\n.footer { margin-top: 30px; padding-top: 12px; border-top: 1px solid #ddd; font-size: 10px; color: #aaa; text-align: center; }\n@media print { body { padding: 0; } .message { break-inside: avoid; } }\n@page { margin: 15mm; }\n"; } private static string FormatContent(string content) { if (string.IsNullOrEmpty(content)) { return ""; } string input = EscapeHtml(content); input = Regex.Replace(input, "```(\\w*)\\n([\\s\\S]*?)```", (Match m) => "
" + m.Groups[2].Value + "
"); input = Regex.Replace(input, "`([^`]+)`", "$1"); return input.Replace("\n", "
"); } private static string EscapeHtml(string text) { return text.Replace("&", "&").Replace("<", "<").Replace(">", ">") .Replace("\"", """); } public static void OpenInBrowser(string htmlPath) { try { Process.Start(new ProcessStartInfo(htmlPath) { UseShellExecute = true }); } catch (Exception ex) { LogService.Warn("PDF 내보내기 브라우저 열기 실패: " + ex.Message); } } }