Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Services/PdfExportService.cs

144 lines
6.7 KiB
C#

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("<!DOCTYPE html>");
stringBuilder.AppendLine("<html lang=\"ko\">");
stringBuilder.AppendLine("<head>");
stringBuilder.AppendLine("<meta charset=\"utf-8\">");
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(28, 1, stringBuilder2);
handler.AppendLiteral("<title>AX Copilot — ");
handler.AppendFormatted(EscapeHtml(conversation.Title));
handler.AppendLiteral("</title>");
stringBuilder3.AppendLine(ref handler);
stringBuilder.AppendLine("<style>");
stringBuilder.AppendLine(GetPrintStyles());
stringBuilder.AppendLine("</style>");
stringBuilder.AppendLine("</head>");
stringBuilder.AppendLine("<body>");
stringBuilder.AppendLine("<div class=\"header\">");
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
handler.AppendLiteral("<h1>");
handler.AppendFormatted(EscapeHtml(conversation.Title));
handler.AppendLiteral("</h1>");
stringBuilder4.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(32, 2, stringBuilder2);
handler.AppendLiteral("<div class=\"meta\">");
handler.AppendFormatted(conversation.CreatedAt, "yyyy-MM-dd HH:mm");
handler.AppendLiteral(" · ");
handler.AppendFormatted(conversation.Messages.Count);
handler.AppendLiteral("개 메시지</div>");
stringBuilder5.AppendLine(ref handler);
stringBuilder.AppendLine("</div>");
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("<div class=\"message ");
handler.AppendFormatted(value);
handler.AppendLiteral("\">");
stringBuilder6.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(24, 1, stringBuilder2);
handler.AppendLiteral("<div class=\"role\">");
handler.AppendFormatted(value2);
handler.AppendLiteral("</div>");
stringBuilder7.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(27, 1, stringBuilder2);
handler.AppendLiteral("<div class=\"content\">");
handler.AppendFormatted(FormatContent(message.Content));
handler.AppendLiteral("</div>");
stringBuilder8.AppendLine(ref handler);
if (message.Timestamp != default(DateTime))
{
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(24, 1, stringBuilder2);
handler.AppendLiteral("<div class=\"time\">");
handler.AppendFormatted(message.Timestamp, "HH:mm");
handler.AppendLiteral("</div>");
stringBuilder9.AppendLine(ref handler);
}
stringBuilder.AppendLine("</div>");
}
stringBuilder.AppendLine("<div class=\"footer\">");
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("</div>");
stringBuilder.AppendLine("<script>window.onload = function() { window.print(); };</script>");
stringBuilder.AppendLine("</body></html>");
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) => "<pre><code>" + m.Groups[2].Value + "</code></pre>");
input = Regex.Replace(input, "`([^`]+)`", "<code>$1</code>");
return input.Replace("\n", "<br>");
}
private static string EscapeHtml(string text)
{
return text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
.Replace("\"", "&quot;");
}
public static void OpenInBrowser(string htmlPath)
{
try
{
Process.Start(new ProcessStartInfo(htmlPath)
{
UseShellExecute = true
});
}
catch (Exception ex)
{
LogService.Warn("PDF 내보내기 브라우저 열기 실패: " + ex.Message);
}
}
}