Skip to content

Commit 0611df8

Browse files
authored
#1184 - Pdf with password (#1185)
- Added password support to the PdfViewer component (new Password parameter) and passed it through JS interop. - Added OnDocumentLoadError event callback and improved error handling for loading documents (including password-related exceptions). - Updated blazor.bootstrap.pdf.js to prompt for passwords and handle password errors. - Updated demos to test password-protected PDFs and switched demo Url to a publicly hosted PDF so it works in non-local environments. - Included a sample password-protected PDF for testing and improved its metadata/font handling. - Updated documentation and images to use GitHub-hosted URLs and added examples showing how to provide a password or prompt users. - General robustness improvements: null checks, better documentation, and minor demo/doc updates.
1 parent ac9f075 commit 0611df8

File tree

8 files changed

+144
-10
lines changed

8 files changed

+144
-10
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@
3535
<Image Src="https://github.com/user-attachments/assets/2a2afd0d-9065-456c-9866-db10f81b7780" Alt="Blazor Bootstrap: Blazor PDF Viewer Component - RTL doc"/>
3636
</Section>
3737

38+
<Section Size="HeadingSize.H2" Name="Password protected" PageUrl="@pageUrl" Link="password-protected">
39+
<div class="mb-3">
40+
To open a password-protected PDF document, set the <code>Password</code> parameter to the required password.
41+
</div>
42+
<Demo Type="typeof(PdfViewer_Demo_05_Password_Protected_A)" Tabs="true" />
43+
</Section>
44+
45+
<Section Size="HeadingSize.H2" Name="Prompt for password" PageUrl="@pageUrl" Link="prompt-for-password">
46+
<div class="mb-3">
47+
If the <code>Password</code> parameter is not set and the PDF document is password-protected, the PDF Viewer component will prompt the user to enter the password.
48+
In the following example, the PDF Viewer prompts the user to enter the password for the protected PDF document. Enter <b>12345</b> as the password to view the document.
49+
</div>
50+
<Demo Type="typeof(PdfViewer_Demo_05_Password_Protected_B_Prompt_For_Password)" Tabs="true" />
51+
</Section>
52+
3853
@code {
3954
private const string pageUrl = RouteConstants.Demos_PDFViewer_Documentation;
4055
private const string pageTitle = "Blazor PDF Viewer";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<PdfViewer Class="mb-3"
2+
Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")"
3+
Password="12345" />
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@if (showPdfViewer)
2+
{
3+
<PdfViewer Class="mb-3" Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")" PromptForPassword="true" />
4+
}
5+
else
6+
{
7+
<Button Class="btn btn-secondary mb-3" @onclick="() => showPdfViewer = true">Load Password Protected PDF (Prompt for Password)</Button>
8+
}
9+
10+
@code {
11+
private bool showPdfViewer = false;
12+
}
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() {

docs/docs/05-components/pdf-viewer.mdx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ The Blazor PDF Viewer component allows users to view PDF files directly in the b
2020
| Name | Type | Default | Required | Description | Added Version |
2121
|:--|:--|:--|:--|:--|:--|
2222
| Orientation | `Orientation` | `Orientation.Portrait` | | Gets or sets the preferred orientation for the PDF viewer. | 2.1.0 |
23+
| Password | string | null | | Gets or sets the password required to open password-protected PDF documents. | 3.5.0 |
2324
| Url | string | null | ✔️ | Gets or sets the URL of the PDF document to be displayed. PDF Viewer component supports base64 string as a URL. | 1.11.0 |
2425

2526
## Callback Events
2627

2728
| Event | Description | Added Version |
2829
|:--|:--|:--|
2930
| OnDocumentLoaded | This event fires immediately after the PDF document is loaded. | 1.11.0 |
31+
| OnDocumentLoadError | This event fires if there is an error loading the PDF document. | 3.5.0 |
3032
| OnPageChanged | This event fires immediately after the page is changed. | 1.11.0 |
3133

3234
## Examples
@@ -148,3 +150,41 @@ Below screenshot is added for demo purposes only. For additional info, refer to
148150
=> eventLog = $"Last event: OnPageChanged, CurrentPage: {args.CurrentPage}, TotalPages: {args.TotalPages}";
149151
}
150152
```
153+
154+
### Password protected
155+
156+
To open a password-protected PDF document, set the <code>Password</code> parameter to the required password.
157+
158+
<img src="https://github.com/user-attachments/assets/92e6634a-eedd-4ad5-b067-858d2cd2282a" alt="Blazor Bootstrap: Blazor PDF Viewer Component - Password protected" />
159+
160+
```cshtml {} showLineNumbers
161+
<PdfViewer Class="mb-3"
162+
Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")"
163+
Password="12345" />
164+
```
165+
166+
[See demo here.](https://demos.blazorbootstrap.com/pdf-viewer#password-protected)
167+
168+
### Prompt for password
169+
170+
If the <code>Password</code> parameter is not set and the PDF document is password-protected, the PDF Viewer component will prompt the user to enter the password.
171+
In the following example, the PDF Viewer prompts the user to enter the password for the protected PDF document. Enter <b>12345</b> as the password to view the document.
172+
173+
<img src="https://github.com/user-attachments/assets/ba4d7a9f-fea7-451e-b31d-72af5d03569e" alt="Blazor Bootstrap: Blazor PDF Viewer Component - Prompt for password" />
174+
175+
```cshtml {} showLineNumbers
176+
@if (showPdfViewer)
177+
{
178+
<PdfViewer Class="mb-3" Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")" PromptForPassword="true" />
179+
}
180+
else
181+
{
182+
<Button Class="btn btn-secondary mb-3" @onclick="() => showPdfViewer = true">Load Password Protected PDF (Prompt for Password)</Button>
183+
}
184+
185+
@code {
186+
private bool showPdfViewer = false;
187+
}
188+
```
189+
190+
[See demo here.](https://demos.blazorbootstrap.com/pdf-viewer#prompt-for-password)

0 commit comments

Comments
 (0)