-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPrintService.cs
More file actions
138 lines (123 loc) · 5.82 KB
/
PrintService.cs
File metadata and controls
138 lines (123 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* In the name of God, the Merciful, the Compassionate */
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;
using Microsoft.Web.WebView2.Core;
namespace SQLTriage.Data.Services
{
// BM:PrintService.Class — exports current page to PDF via WebView2 or browser print
/// <summary>
/// Wraps CoreWebView2.PrintToPdfAsync to export the current page as a multi-page PDF,
/// respecting @media print CSS rules. Saves to .\output\ automatically.
/// Falls back to browser window.print() in server mode (no WebView2).
/// </summary>
public class PrintService : IPrintService
{
private CoreWebView2? _webView;
private readonly AuditLogService? _auditLog;
private readonly ILogger<PrintService> _logger;
/// <summary>
/// Constructor with optional AuditLogService for audit trail.
/// </summary>
public PrintService(ILogger<PrintService> logger, AuditLogService? auditLog = null)
{
_logger = logger;
_auditLog = auditLog;
}
public void SetWebView(CoreWebView2 webView)
{
_webView = webView;
}
public bool IsAvailable => _webView != null;
/// <summary>
/// Exports the current WebView page to PDF in the .\output\ folder.
/// printBackgrounds: true preserves dark-theme colours (dashboards);
/// false forces a white print (Quick Check / text reports).
/// Returns (true, path, null) on success, (false, null, error) if unavailable or failed.
/// </summary>
public async Task<(bool Success, string? Path, string? Error)> PrintToPdfAsync(
string fileName = "Report.pdf",
bool printBackgrounds = false,
CoreWebView2PrintOrientation orientation = CoreWebView2PrintOrientation.Landscape,
string? headerTitle = null,
string? footerUri = null,
int timeoutSeconds = 30)
{
if (_webView == null)
{
_auditLog?.LogExportOperation("PDF", fileName, false, "WebView not available");
return (false, null, "WebView not available — use browser print (Ctrl+P) or call PrintViaBrowserAsync.");
}
var outputDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "output");
Directory.CreateDirectory(outputDir);
var filePath = System.IO.Path.Combine(outputDir, fileName);
// RunContinuationsAsynchronously prevents inline continuation deadlocks
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
// All WebView2 API calls (CreatePrintSettings + PrintToPdfAsync) must run on the WPF UI thread.
Application.Current.Dispatcher.InvokeAsync(async () =>
{
try
{
var settings = _webView.Environment.CreatePrintSettings();
settings.Orientation = orientation;
settings.ShouldPrintBackgrounds = printBackgrounds;
settings.ShouldPrintHeaderAndFooter = headerTitle != null || footerUri != null;
if (headerTitle != null) settings.HeaderTitle = headerTitle;
if (footerUri != null) settings.FooterUri = footerUri;
// 5 mm margins all round (~0.2 in); bottom slightly taller for DOM footer bar
settings.MarginTop = 0.2;
settings.MarginBottom = 0.25;
settings.MarginLeft = 0.2;
settings.MarginRight = 0.2;
await _webView.PrintToPdfAsync(filePath, settings);
tcs.TrySetResult(true);
}
catch (System.Exception ex)
{
tcs.TrySetException(ex);
}
});
try
{
using var cts = new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
var timeoutTask = Task.Delay(Timeout.Infinite, cts.Token);
var completed = await Task.WhenAny(tcs.Task, timeoutTask);
if (completed == timeoutTask)
{
_logger.LogError("PDF export timed out after {Seconds}s for file {FileName}", timeoutSeconds, fileName);
_auditLog?.LogExportOperation("PDF", fileName, false, $"Timed out after {timeoutSeconds}s");
return (false, null, $"PDF export timed out after {timeoutSeconds} seconds.");
}
await tcs.Task; // unwrap any exception
_auditLog?.LogExportOperation("PDF", fileName, true);
return (true, filePath, null);
}
catch (System.Exception ex)
{
_auditLog?.LogExportOperation("PDF", fileName, false, ex.Message);
_logger.LogError(ex, "PDF export failed for file {FileName}", fileName);
return (false, null, ex.Message);
}
}
/// <summary>
/// Falls back to the browser's native print dialog via JS interop.
/// Used in server mode where WebView2 is not available.
/// </summary>
public async Task<(bool Success, string? Path, string? Error)> PrintViaBrowserAsync(IJSRuntime jsRuntime)
{
try
{
await jsRuntime.InvokeVoidAsync("window.print");
_auditLog?.LogExportOperation("PDF", "browser-print", true);
return (true, null, null);
}
catch (Exception ex)
{
_logger.LogError(ex, "Browser print failed");
return (false, null, ex.Message);
}
}
}
}