Skip to content

Commit d33b0d4

Browse files
authored
feat(PdfReader): add OnPageChangedAsync parameter (#715)
* feat(PdfReader): 增加页码回调方法 * feat: 增加 OnPageChangedAsync 回调方法
1 parent 034634a commit d33b0d4

5 files changed

Lines changed: 194 additions & 92 deletions

File tree

src/components/BootstrapBlazor.PdfReader/PdfReader.razor

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,34 @@
33
@inherits BootstrapModuleComponentBase
44

55
<div @attributes="@AdditionalAttributes" id="@Id" class="@ClassString" style="@StyleString">
6-
<div class="bb-view-toolbar">
7-
<div class="bb-view-title">
8-
<div class="bb-view-icon bb-view-bar"><i class="fa-solid fa-bars"></i></div>
9-
<span class="bb-view-subject">@_docTitle</span>
6+
@if (Options.ShowToolbar)
7+
{
8+
<div class="bb-view-toolbar init">
9+
<div class="bb-view-title">
10+
<div class="bb-view-icon bb-view-bar"><i class="fa-solid fa-bars"></i></div>
11+
<span class="bb-view-subject">@_docTitle</span>
12+
</div>
13+
<div class="@ViewBodyString">
14+
<input type="text" class="bb-view-num" @bind="CurrentPageString" /><span class="bb-view-slash">/</span><div class="bb-view-pagesCount"></div>
15+
<div class="bb-view-divider"></div>
16+
<div class="bb-view-icon"><i class="fa-solid fa-minus"></i></div>
17+
<input type="text" class="bb-view-scale" value="100%" />
18+
<div class="bb-view-icon"><i class="fa-solid fa-plus"></i></div>
19+
<div class="bb-view-divider"></div>
20+
<div class="bb-view-icon bb-view-fit-page" @onclick="FitToPage"><i class="fa-solid fa-arrows-left-right-to-line"></i></div>
21+
<div class="bb-view-icon bb-view-fit-width" @onclick="FitToWidth"><i class="fa-solid fa-arrows-left-right"></i></div>
22+
<div class="bb-view-icon bb-view-fit-rotate" @onclick="RotateLeft"><i class="fa-solid fa-rotate-left"></i></div>
23+
<div class="bb-view-icon bb-view-fit-rotate" @onclick="RotateRight"><i class="fa-solid fa-rotate-right"></i></div>
24+
<div class="bb-view-divider"></div>
25+
<div class="bb-view-icon bb-view-draw"><i class="fa-solid fa-pen-to-square"></i></div>
26+
</div>
27+
<div class="bb-view-controls">
28+
<div class="bb-view-icon bb-view-download"><i class="fa-solid fa-arrow-right-to-bracket fa-rotate-90"></i></div>
29+
<div class="bb-view-icon bb-view-print"><i class="fa-solid fa-print"></i></div>
30+
<div class="bb-view-icon bb-view-home"><i class="fa-solid fa-flag"></i></div>
31+
</div>
1032
</div>
11-
<div class="@ViewBodyString">
12-
<input type="text" class="bb-view-num" value="1" /><span class="bb-view-slash">/</span><div>14</div>
13-
<div class="bb-view-divider"></div>
14-
<div class="bb-view-icon"><i class="fa-solid fa-minus"></i></div>
15-
<input type="text" class="bb-view-scale" value="100%" />
16-
<div class="bb-view-icon"><i class="fa-solid fa-plus"></i></div>
17-
<div class="bb-view-divider"></div>
18-
<div class="bb-view-icon bb-view-fit-page" @onclick="FitToPage"><i class="fa-solid fa-arrows-left-right-to-line"></i></div>
19-
<div class="bb-view-icon bb-view-fit-width" @onclick="FitToWidth"><i class="fa-solid fa-arrows-left-right"></i></div>
20-
<div class="bb-view-icon bb-view-fit-rotate" @onclick="RotateLeft"><i class="fa-solid fa-rotate-left"></i></div>
21-
<div class="bb-view-icon bb-view-fit-rotate" @onclick="RotateRight"><i class="fa-solid fa-rotate-right"></i></div>
22-
<div class="bb-view-divider"></div>
23-
<div class="bb-view-icon bb-view-draw"><i class="fa-solid fa-pen-to-square"></i></div>
24-
</div>
25-
<div class="bb-view-controls">
26-
<div class="bb-view-icon bb-view-download"><i class="fa-solid fa-arrow-right-to-bracket fa-rotate-90"></i></div>
27-
<div class="bb-view-icon bb-view-print"><i class="fa-solid fa-print"></i></div>
28-
<div class="bb-view-icon bb-view-home"><i class="fa-solid fa-flag"></i></div>
29-
</div>
30-
</div>
33+
}
3134
<div class="bb-view-container">
3235
<div class="pdfViewer"></div>
3336
</div>

src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Website: https://www.blazor.zone or https://argozhang.github.io/
44

55
using Microsoft.AspNetCore.Components;
6+
using System.Globalization;
67

78
namespace BootstrapBlazor.Components;
89

@@ -13,38 +14,43 @@ namespace BootstrapBlazor.Components;
1314
public partial class PdfReader
1415
{
1516
/// <summary>
16-
/// 获得/设置 PDF 文档路径
17+
/// 获得/设置 <see cref="PdfReaderOptions"/> 配置项实例
1718
/// </summary>
1819
[Parameter]
19-
public string? Url { get; set; }
20-
21-
/// <summary>
22-
/// 获得/设置 PDF 组件高度 默认 600px
23-
/// </summary>
24-
[Parameter]
25-
public string? ViewHeight { get; set; }
26-
27-
/// <summary>
28-
/// 获得/设置 是否适配当前页面宽度 默认 false
29-
/// </summary>
30-
[Parameter]
31-
public bool IsFitToPage { get; set; }
20+
[NotNull]
21+
public PdfReaderOptions? Options { get; set; }
3222

3323
private string? ClassString => CssBuilder.Default("bb-pdf-reader")
3424
.AddClassFromAttributes(AdditionalAttributes)
3525
.Build();
3626

3727
private string? StyleString => CssBuilder.Default()
38-
.AddClass($"--bb-pdf-view-height: {ViewHeight};", !string.IsNullOrEmpty(ViewHeight))
28+
.AddClass($"--bb-pdf-view-height: {Options.ViewHeight};", !string.IsNullOrEmpty(Options.ViewHeight))
3929
.AddClassFromAttributes(AdditionalAttributes)
4030
.Build();
4131

4232
private string? ViewBodyString => CssBuilder.Default("bb-view-body")
43-
.AddClass("fit-page", IsFitToPage)
33+
.AddClass("fit-page", Options.IsFitToPage)
4434
.Build();
4535

4636
private string? _docTitle;
4737
private bool _isFitToPage;
38+
private uint _currentPage;
39+
private string? _url;
40+
41+
private string CurrentPageString
42+
{
43+
get => Options.CurrentPage.ToString(CultureInfo.InvariantCulture);
44+
set => SetCurrentPage(value);
45+
}
46+
47+
private void SetCurrentPage(string value)
48+
{
49+
if (uint.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
50+
{
51+
Options.CurrentPage = num;
52+
}
53+
}
4854

4955
/// <summary>
5056
/// <inheritdoc/>
@@ -53,7 +59,13 @@ protected override void OnParametersSet()
5359
{
5460
base.OnParametersSet();
5561

56-
_docTitle = Path.GetFileName(Url);
62+
Options ??= new PdfReaderOptions();
63+
64+
if (Options.CurrentPage == 0)
65+
{
66+
Options.CurrentPage = 1;
67+
}
68+
_docTitle = Path.GetFileName(Options.Url);
5769
}
5870

5971
/// <summary>
@@ -67,38 +79,57 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
6779

6880
if (firstRender)
6981
{
70-
_isFitToPage = IsFitToPage;
82+
_isFitToPage = Options.IsFitToPage;
83+
_currentPage = Options.CurrentPage;
84+
_url = Options.Url;
7185
}
7286

73-
if (_isFitToPage != IsFitToPage)
87+
if (_url != Options.Url)
7488
{
75-
_isFitToPage = IsFitToPage;
76-
await TriggerFit(IsFitToPage ? "fitToPage" : "fitToWidth");
89+
_url = Options.Url;
90+
await InvokeInitAsync();
91+
}
92+
93+
if (_isFitToPage != Options.IsFitToPage)
94+
{
95+
_isFitToPage = Options.IsFitToPage;
96+
await TriggerFit(_isFitToPage ? "fitToPage" : "fitToWidth");
97+
}
98+
if (_currentPage != Options.CurrentPage)
99+
{
100+
_currentPage = Options.CurrentPage;
101+
await NavigateToPageAsync(_currentPage);
77102
}
78103
}
79104

80105
/// <summary>
81106
/// <inheritdoc/>
82107
/// </summary>
83108
/// <returns></returns>
84-
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new { Url, IsFitToPage });
109+
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new
110+
{
111+
Options.Url,
112+
Options.IsFitToPage,
113+
TriggerPagesInit = Options.OnInitAsync != null,
114+
TriggerPageChanged = Options.OnPageChangedAsync != null
115+
});
85116

86117
/// <summary>
87118
/// 跳转到指定页码方法
88119
/// </summary>
89120
/// <param name="pageNumber"></param>
90121
/// <returns></returns>
91-
public Task NavigateToPageAsync(int pageNumber) => InvokeVoidAsync("navigateToPage", Id, pageNumber);
122+
public Task NavigateToPageAsync(uint pageNumber) => InvokeVoidAsync("navigateToPage", Id, pageNumber);
92123

93124
/// <summary>
94125
/// 适应页面宽度
95126
/// </summary>
96-
public void FitToPage() => IsFitToPage = true;
127+
public void FitToPage() => Options.IsFitToPage = true;
97128

98129
/// <summary>
99130
/// 适应文档宽度
100131
/// </summary>
101-
public void FitToWidth() => IsFitToPage = false;
132+
public void FitToWidth() => Options.IsFitToPage = false;
102133

103134
/// <summary>
104135
/// 旋转页面方法
@@ -125,18 +156,27 @@ public async Task RotateRight()
125156
/// </summary>
126157
/// <returns></returns>
127158
[JSInvokable]
128-
public Task PagesInit()
159+
public async Task PagesInit(int pagesCount)
129160
{
130-
return Task.CompletedTask;
161+
if (Options.OnInitAsync != null)
162+
{
163+
await Options.OnInitAsync(pagesCount);
164+
}
131165
}
132166

133167
/// <summary>
134168
/// 改变页码时回调方法
135169
/// </summary>
136170
/// <returns></returns>
137171
[JSInvokable]
138-
public Task PageChanging()
172+
public async Task PageChanged(uint pageIndex)
139173
{
140-
return Task.CompletedTask;
174+
_currentPage = pageIndex;
175+
Options.CurrentPage = pageIndex;
176+
177+
if (Options.OnPageChangedAsync != null)
178+
{
179+
await Options.OnPageChangedAsync(pageIndex);
180+
}
141181
}
142182
}

src/components/BootstrapBlazor.PdfReader/wwwroot/css/pdf.css renamed to src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@import url('pdf_viewer.css');
2-
31
.bb-pdf-reader {
42
--bb-pdf-view-height: 600px;
53
--bb-pdf-toolbar-height: 60px;
@@ -16,6 +14,10 @@
1614
color: #fff;
1715
}
1816

17+
.bb-view-toolbar.init > div {
18+
visibility: hidden;
19+
}
20+
1921
.bb-view-title {
2022
display: flex;
2123
align-items: center;

src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,28 @@ if (pdfjsLib != null) {
88
}
99

1010
export async function init(id, invoke, options) {
11+
await addLink('./_content/BootstrapBlazor.PdfReader/css/pdf_viewer.css');
12+
1113
const el = document.getElementById(id);
1214
if (el === null) {
1315
return;
1416
}
1517

16-
await addLink('./_content/BootstrapBlazor.PdfReader/css/pdf.css');
18+
if (options.url === null) {
19+
return;
20+
}
1721

1822
const loadingTask = pdfjsLib.getDocument(options);
1923
loadingTask.onProgress = function (progressData) {
2024
console.log(progressData.loaded, progressData.total);
2125
};
2226

23-
// handle password only when required (optional password support)
2427
loadingTask.onPassword = function (updatePassword, reason) {
2528
if (reason === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
26-
// only prompt if PDF actually requires password
2729
const password = prompt("This PDF is password protected. Enter password:");
2830
updatePassword(password);
29-
} else if (reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
31+
}
32+
else if (reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
3033
const password = prompt("Incorrect password. Please try again:");
3134
updatePassword(password);
3235
}
@@ -39,43 +42,11 @@ export async function init(id, invoke, options) {
3942
eventBus
4043
});
4144

45+
addEventListener(pdfViewer, eventBus, invoke, options);
4246

43-
eventBus.on("pagesinit", function () {
44-
if (options.isFitToPage) {
45-
pdfViewer.currentScaleValue = "page-width";
46-
}
47-
else {
48-
pdfViewer.currentScaleValue = 1.0;
49-
}
50-
});
51-
52-
// handle the promise
5347
const pdfDocument = await loadingTask.promise;
5448
pdfViewer.setDocument(pdfDocument);
5549

56-
// pdfDocument.then(function (doc) {
57-
// pdf.pdfDoc = doc;
58-
// pdf.pagesCount = doc.numPages;
59-
// renderPage(pdf, pdf.pageNum);
60-
61-
// // notify .NET side that document is loaded
62-
// invoke.invokeMethodAsync('DocumentLoaded', {
63-
// pagesCount: pdf.pagesCount,
64-
// pageNumber: pdf.pageNum
65-
// });
66-
// })
67-
// .catch(function (error) {
68-
// console.error("PDF loading error:", error);
69-
70-
// // handle password exceptions specifically
71-
// if (error.name === "PasswordException") {
72-
// console.error("Password required but not provided");
73-
// }
74-
75-
// // notify .NET side that document loading failed
76-
// invoke.invokeMethodAsync('DocumentLoadError', error.message);
77-
// });
78-
7950
Data.set(id, pdfViewer);
8051
}
8152

@@ -106,6 +77,46 @@ export function dispose(id) {
10677
Data.get(id);
10778
}
10879

80+
const addEventListener = (pdfViewer, eventBus, invoke, options) => {
81+
eventBus.on("pagesinit", async () => {
82+
if (options.isFitToPage) {
83+
pdfViewer.currentScaleValue = "page-width";
84+
}
85+
else {
86+
pdfViewer.currentScaleValue = 1.0;
87+
}
88+
89+
const el = pdfViewer.container.parentElement;
90+
const numPages = pdfViewer.pagesCount;
91+
const countEl = el.querySelector(".bb-view-pagesCount");
92+
if (countEl) {
93+
countEl.innerHTML = numPages;
94+
}
95+
96+
const toolbarEl = el.querySelector(".bb-view-toolbar");
97+
if (toolbarEl) {
98+
toolbarEl.classList.remove("init");
99+
}
100+
101+
if (options.triggerPagesInit === true) {
102+
await invoke.invokeMethodAsync("pagesInit", numPages);
103+
}
104+
});
105+
106+
eventBus.on("pagechanging", async evt => {
107+
const page = evt.pageNumber;
108+
const el = evt.source.container.parentElement;
109+
const pageNumberEl = el.querySelector(".bb-view-num");
110+
if (pageNumberEl) {
111+
pageNumberEl.value = page;
112+
}
113+
114+
if (options.triggerPageChanged === true) {
115+
await invoke.invokeMethodAsync("pageChanged", page);
116+
}
117+
}, true);
118+
}
119+
109120
function getCanvas(item) {
110121
if (isDomSupported() && typeof item === 'string') {
111122
item = document.getElementById(item);

0 commit comments

Comments
 (0)