Skip to content

Commit d3babfe

Browse files
committed
Add support for password-protected PDFs
Enhanced the `PdfViewer` component to support password-protected PDFs and improved error handling for document loading. Key changes include: - Added a `Password` parameter to the `PdfViewer` component and updated the `OnAfterRenderAsync` method to pass it to JavaScript interop. - Introduced the `OnDocumentLoadError` event callback to handle document loading errors. - Updated `blazor.bootstrap.pdf.js` to support password prompts and handle password-related exceptions. - Modified `PdfViewer_Demo_01_Examples.razor` to test password-protected PDFs with a local file path. - Added a sample encrypted PDF (`pdf_password_protected.pdf`) for testing. - Improved metadata and font handling in the sample PDF. - Enhanced code robustness with null checks and better documentation. NOTE: This commit message is auto-generated using GitHub Copilot.
1 parent ac9f075 commit d3babfe

File tree

5 files changed

+75
-11
lines changed

5 files changed

+75
-11
lines changed

BlazorBootstrap.Demo.RCL/Components/Pages/PdfViewer/PdfViewer_Demo_01_Examples.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<p>@eventLog</p>
22

33
<PdfViewer Class="mb-3"
4-
Url="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf"
4+
Url="file:///D:/Badri/Scanned%20Docs/Badri_EAadhaar_GADD2014.pdf"
55
OnDocumentLoaded="OnDocumentLoaded"
66
OnPageChanged="OnPageChanged" />
77

14.2 KB
Binary file not shown.

blazorbootstrap/Components/PdfViewer/PdfViewer.razor.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public partial class PdfViewer : BlazorBootstrapComponentBase
3232
protected override async Task OnAfterRenderAsync(bool firstRender)
3333
{
3434
if (firstRender)
35-
await PdfViewerJsInterop.InitializeAsync(objRef!, Id!, scale, rotation, Url!);
35+
await PdfViewerJsInterop.InitializeAsync(objRef!, Id!, scale, rotation, Url!, Password!);
3636

3737
await base.OnAfterRenderAsync(firstRender);
3838
}
@@ -73,6 +73,15 @@ public void DocumentLoaded(PdfViewerModel pdfViewerModel)
7373
OnDocumentLoaded.InvokeAsync(new PdfViewerEventArgs(pageNumber, pagesCount));
7474
}
7575

76+
[JSInvokable]
77+
public void DocumentLoadError(string errorMessage)
78+
{
79+
if(string.IsNullOrEmpty(errorMessage)) return;
80+
81+
if (OnDocumentLoadError.HasDelegate)
82+
OnDocumentLoadError.InvokeAsync(errorMessage);
83+
}
84+
7685
[JSInvokable]
7786
public void SetPdfViewerMetaData(PdfViewerModel pdfViewerModel)
7887
{
@@ -207,6 +216,12 @@ private async Task ZoomOutAsync()
207216
[Parameter]
208217
public EventCallback<PdfViewerEventArgs> OnDocumentLoaded { get; set; }
209218

219+
/// <summary>
220+
/// This event fires if there is an error loading the PDF document.
221+
/// </summary>
222+
[Parameter]
223+
public EventCallback<string> OnDocumentLoadError { get; set; }
224+
210225
/// <summary>
211226
/// This event fires immediately after the page is changed.
212227
/// </summary>
@@ -222,6 +237,15 @@ private async Task ZoomOutAsync()
222237
[Parameter]
223238
public Orientation Orientation { get; set; } = Orientation.Portrait;
224239

240+
/// <summary>
241+
/// Gets or sets the password used for the PDF document if it is password-protected.
242+
/// </summary>
243+
/// <remarks>
244+
/// Default value is <see langword="null"/>.
245+
/// </remarks>
246+
[Parameter]
247+
public string? Password { get; set; }
248+
225249
/// <summary>
226250
/// Provides JavaScript interop functionality for the PDF viewer.
227251
/// </summary>

blazorbootstrap/Components/PdfViewer/PdfViewerJsInterop.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ public async Task GotoPageAsync(object objRef, string elementId, int gotoPageNum
4040
await module.InvokeVoidAsync("gotoPage", objRef, elementId, gotoPageNum);
4141
}
4242

43-
public async Task InitializeAsync(object objRef, string elementId, double scale, double rotation, string url)
43+
public async Task InitializeAsync(object objRef, string elementId, double scale, double rotation, string url, string password)
4444
{
4545
var module = await moduleTask.Value;
46-
await module.InvokeVoidAsync("initialize", objRef, elementId, scale, rotation, url);
46+
await module.InvokeVoidAsync("initialize", objRef, elementId, scale, rotation, url, password);
4747
}
4848

4949
public async Task LastPageAsync(object objRef, string elementId)

blazorbootstrap/wwwroot/blazor.bootstrap.pdf.js

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,57 @@ pageRotateCwButton.disabled = this.pagesCount === 0;
194194
pageRotateCcwButton.disabled = this.pagesCount === 0;
195195
*/
196196

197-
export function initialize(dotNetHelper, elementId, scale, rotation, url) {
197+
export function initialize(dotNetHelper, elementId, scale, rotation, url, password = null) {
198198
const pdf = new Pdf(elementId);
199199
pdf.scale = scale;
200200
pdf.rotation = rotation;
201201

202-
pdfJS.getDocument(url).promise.then(function (doc) {
203-
pdf.pdfDoc = doc;
204-
pdf.pagesCount = doc.numPages;
205-
renderPage(pdf, pdf.pageNum);
206-
dotNetHelper.invokeMethodAsync('DocumentLoaded', { pagesCount: pdf.pagesCount, pageNumber: pdf.pageNum });
207-
});
202+
// prepare loading options (optional password allowed)
203+
const options = { url };
204+
if (password) {
205+
options.password = password; // pre-supply if user already has it
206+
}
207+
208+
// begin loading document
209+
const loadingTask = pdfJS.getDocument(options);
210+
211+
// handle password only when required (optional password support)
212+
loadingTask.onPassword = function (updatePassword, reason) {
213+
if (reason === pdfJS.PasswordResponses.NEED_PASSWORD) {
214+
// only prompt if PDF actually requires password
215+
const password = prompt("This PDF is password protected. Enter password:");
216+
updatePassword(password);
217+
} else if (reason === pdfJS.PasswordResponses.INCORRECT_PASSWORD) {
218+
const password = prompt("Incorrect password. Please try again:");
219+
updatePassword(password);
220+
}
221+
};
222+
223+
// handle the promise
224+
loadingTask
225+
.promise
226+
.then(function (doc) {
227+
pdf.pdfDoc = doc;
228+
pdf.pagesCount = doc.numPages;
229+
renderPage(pdf, pdf.pageNum);
230+
231+
// notify .NET side that document is loaded
232+
dotNetHelper.invokeMethodAsync('DocumentLoaded', {
233+
pagesCount: pdf.pagesCount,
234+
pageNumber: pdf.pageNum
235+
});
236+
})
237+
.catch(function (error) {
238+
console.error("PDF loading error:", error);
239+
240+
// handle password exceptions specifically
241+
if (error.name === "PasswordException") {
242+
console.error("Password required but not provided");
243+
}
244+
245+
// notify .NET side that document loading failed
246+
dotNetHelper.invokeMethodAsync('DocumentLoadError', error.message);
247+
});
208248
}
209249

210250
function isDomSupported() {

0 commit comments

Comments
 (0)