Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@
<Image Src="https://github.com/user-attachments/assets/2a2afd0d-9065-456c-9866-db10f81b7780" Alt="Blazor Bootstrap: Blazor PDF Viewer Component - RTL doc"/>
</Section>

<Section Size="HeadingSize.H2" Name="Password protected" PageUrl="@pageUrl" Link="password-protected">
<div class="mb-3">
To open a password-protected PDF document, set the <code>Password</code> parameter to the required password.
</div>
<Demo Type="typeof(PdfViewer_Demo_05_Password_Protected_A)" Tabs="true" />
</Section>

<Section Size="HeadingSize.H2" Name="Prompt for password" PageUrl="@pageUrl" Link="prompt-for-password">
<div class="mb-3">
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.
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.
</div>
<Demo Type="typeof(PdfViewer_Demo_05_Password_Protected_B_Prompt_For_Password)" Tabs="true" />
</Section>

@code {
private const string pageUrl = RouteConstants.Demos_PDFViewer_Documentation;
private const string pageTitle = "Blazor PDF Viewer";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<PdfViewer Class="mb-3"
Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")"
Password="12345" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@if (showPdfViewer)
{
<PdfViewer Class="mb-3" Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")" PromptForPassword="true" />
}
else
{
<Button Class="btn btn-secondary mb-3" @onclick="() => showPdfViewer = true">Load Password Protected PDF (Prompt for Password)</Button>
}

@code {
private bool showPdfViewer = false;
}
Binary file not shown.
26 changes: 25 additions & 1 deletion blazorbootstrap/Components/PdfViewer/PdfViewer.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public partial class PdfViewer : BlazorBootstrapComponentBase
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await PdfViewerJsInterop.InitializeAsync(objRef!, Id!, scale, rotation, Url!);
await PdfViewerJsInterop.InitializeAsync(objRef!, Id!, scale, rotation, Url!, Password!);

await base.OnAfterRenderAsync(firstRender);
}
Expand Down Expand Up @@ -73,6 +73,15 @@ public void DocumentLoaded(PdfViewerModel pdfViewerModel)
OnDocumentLoaded.InvokeAsync(new PdfViewerEventArgs(pageNumber, pagesCount));
}

[JSInvokable]
public void DocumentLoadError(string errorMessage)
{
if(string.IsNullOrEmpty(errorMessage)) return;

if (OnDocumentLoadError.HasDelegate)
OnDocumentLoadError.InvokeAsync(errorMessage);
}

[JSInvokable]
public void SetPdfViewerMetaData(PdfViewerModel pdfViewerModel)
{
Expand Down Expand Up @@ -207,6 +216,12 @@ private async Task ZoomOutAsync()
[Parameter]
public EventCallback<PdfViewerEventArgs> OnDocumentLoaded { get; set; }

/// <summary>
/// This event fires if there is an error loading the PDF document.
/// </summary>
[Parameter]
public EventCallback<string> OnDocumentLoadError { get; set; }

/// <summary>
/// This event fires immediately after the page is changed.
/// </summary>
Expand All @@ -222,6 +237,15 @@ private async Task ZoomOutAsync()
[Parameter]
public Orientation Orientation { get; set; } = Orientation.Portrait;

/// <summary>
/// Gets or sets the password used for the PDF document if it is password-protected.
/// </summary>
/// <remarks>
/// Default value is <see langword="null"/>.
/// </remarks>
[Parameter]
public string? Password { get; set; }

/// <summary>
/// Provides JavaScript interop functionality for the PDF viewer.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions blazorbootstrap/Components/PdfViewer/PdfViewerJsInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ public async Task GotoPageAsync(object objRef, string elementId, int gotoPageNum
await module.InvokeVoidAsync("gotoPage", objRef, elementId, gotoPageNum);
}

public async Task InitializeAsync(object objRef, string elementId, double scale, double rotation, string url)
public async Task InitializeAsync(object objRef, string elementId, double scale, double rotation, string url, string password)
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync("initialize", objRef, elementId, scale, rotation, url);
await module.InvokeVoidAsync("initialize", objRef, elementId, scale, rotation, url, password);
}

public async Task LastPageAsync(object objRef, string elementId)
Expand Down
54 changes: 47 additions & 7 deletions blazorbootstrap/wwwroot/blazor.bootstrap.pdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,57 @@ pageRotateCwButton.disabled = this.pagesCount === 0;
pageRotateCcwButton.disabled = this.pagesCount === 0;
*/

export function initialize(dotNetHelper, elementId, scale, rotation, url) {
export function initialize(dotNetHelper, elementId, scale, rotation, url, password = null) {
const pdf = new Pdf(elementId);
pdf.scale = scale;
pdf.rotation = rotation;

pdfJS.getDocument(url).promise.then(function (doc) {
pdf.pdfDoc = doc;
pdf.pagesCount = doc.numPages;
renderPage(pdf, pdf.pageNum);
dotNetHelper.invokeMethodAsync('DocumentLoaded', { pagesCount: pdf.pagesCount, pageNumber: pdf.pageNum });
});
// prepare loading options (optional password allowed)
const options = { url };
if (password) {
options.password = password; // pre-supply if user already has it
}

// begin loading document
const loadingTask = pdfJS.getDocument(options);

// handle password only when required (optional password support)
loadingTask.onPassword = function (updatePassword, reason) {
if (reason === pdfJS.PasswordResponses.NEED_PASSWORD) {
// only prompt if PDF actually requires password
const password = prompt("This PDF is password protected. Enter password:");
updatePassword(password);
} else if (reason === pdfJS.PasswordResponses.INCORRECT_PASSWORD) {
const password = prompt("Incorrect password. Please try again:");
updatePassword(password);
}
};

// handle the promise
loadingTask
.promise
.then(function (doc) {
pdf.pdfDoc = doc;
pdf.pagesCount = doc.numPages;
renderPage(pdf, pdf.pageNum);

// notify .NET side that document is loaded
dotNetHelper.invokeMethodAsync('DocumentLoaded', {
pagesCount: pdf.pagesCount,
pageNumber: pdf.pageNum
});
})
.catch(function (error) {
console.error("PDF loading error:", error);

// handle password exceptions specifically
if (error.name === "PasswordException") {
console.error("Password required but not provided");
}

// notify .NET side that document loading failed
dotNetHelper.invokeMethodAsync('DocumentLoadError', error.message);
});
}

function isDomSupported() {
Expand Down
40 changes: 40 additions & 0 deletions docs/docs/05-components/pdf-viewer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ The Blazor PDF Viewer component allows users to view PDF files directly in the b
| Name | Type | Default | Required | Description | Added Version |
|:--|:--|:--|:--|:--|:--|
| Orientation | `Orientation` | `Orientation.Portrait` | | Gets or sets the preferred orientation for the PDF viewer. | 2.1.0 |
| Password | string | null | | Gets or sets the password required to open password-protected PDF documents. | 3.5.0 |
| 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 |

## Callback Events

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

## Examples
Expand Down Expand Up @@ -148,3 +150,41 @@ Below screenshot is added for demo purposes only. For additional info, refer to
=> eventLog = $"Last event: OnPageChanged, CurrentPage: {args.CurrentPage}, TotalPages: {args.TotalPages}";
}
```

### Password protected

To open a password-protected PDF document, set the <code>Password</code> parameter to the required password.

<img src="https://github.com/user-attachments/assets/92e6634a-eedd-4ad5-b067-858d2cd2282a" alt="Blazor Bootstrap: Blazor PDF Viewer Component - Password protected" />

```cshtml {} showLineNumbers
<PdfViewer Class="mb-3"
Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")"
Password="12345" />
```

[See demo here.](https://demos.blazorbootstrap.com/pdf-viewer#password-protected)

### Prompt for password

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.
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.

<img src="https://github.com/user-attachments/assets/ba4d7a9f-fea7-451e-b31d-72af5d03569e" alt="Blazor Bootstrap: Blazor PDF Viewer Component - Prompt for password" />

```cshtml {} showLineNumbers
@if (showPdfViewer)
{
<PdfViewer Class="mb-3" Url="@($"{StringConstants.DocsBasePath}/pdf_password_protected.pdf")" PromptForPassword="true" />
}
else
{
<Button Class="btn btn-secondary mb-3" @onclick="() => showPdfViewer = true">Load Password Protected PDF (Prompt for Password)</Button>
}

@code {
private bool showPdfViewer = false;
}
```

[See demo here.](https://demos.blazorbootstrap.com/pdf-viewer#prompt-for-password)
Loading