Skip to content

feat(PdfReader): add OnRotationChanged parameter#758

Merged
ArgoZhang merged 1 commit intomasterfrom
feat-reader
Nov 30, 2025
Merged

feat(PdfReader): add OnRotationChanged parameter#758
ArgoZhang merged 1 commit intomasterfrom
feat-reader

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Nov 30, 2025

Link issues

fixes #757

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Add rotation state and callbacks to PdfReader to support reacting to page rotation changes.

New Features:

  • Expose a CurrentRotation parameter on PdfReader to control the current page rotation.
  • Provide an OnRotationChanged callback parameter to notify consumers when the PDF rotation changes.

Enhancements:

  • Synchronize PdfReader rotation state with the underlying viewer and JavaScript layer, including updating thumbnails and invoking .NET callbacks on rotation changes.

Copilot AI review requested due to automatic review settings November 30, 2025 01:08
@bb-auto bb-auto Bot added the enhancement New feature or request label Nov 30, 2025
@bb-auto bb-auto Bot added this to the v9.2.0 milestone Nov 30, 2025
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Nov 30, 2025

Reviewer's Guide

Adds rotation angle state and rotation-changed callback support to the PdfReader component, wiring it through Blazor parameters, JS interop, and the underlying PDF.js event bus.

Sequence diagram for PdfReader rotation change handling

sequenceDiagram
    actor User
    participant PdfViewer as Pdf.js_viewer
    participant EventBus as PdfJs_eventBus
    participant JsModule as PdfReader_js
    participant DotNet as PdfReader_component
    participant Host as Host_app_callback

    User->>PdfViewer: Rotate page (UI control)
    PdfViewer-->>EventBus: rotationchanging evt
    EventBus-->>JsModule: rotationchanging evt
    alt options.triggerRotationChanged is true
        JsModule->>DotNet: invokeMethodAsync RotationChanged(evt.pagesRotation)
        DotNet->>DotNet: RotationChanged(angle)
        alt OnRotationChanged is not null
            DotNet->>Host: OnRotationChanged(angle)
            Host-->>DotNet: Task completion
        end
    end

    rect rgb(230,230,250)
    User->>Host: Set CurrentRotation parameter
    Host->>DotNet: Render PdfReader with CurrentRotation
    DotNet->>DotNet: OnAfterRenderAsync(firstRender or update)
    DotNet->>JsModule: rotate(Id, _currentRotation)
    JsModule-->>PdfViewer: Apply rotation
    end
Loading

Class diagram for updated PdfReader rotation support

classDiagram
    class PdfReader {
        +uint CurrentPage
        +int CurrentRotation
        +bool FitWidth
        +Func_float_Task OnScaleChangedAsync()
        +Func_int_Task OnRotationChanged()
        -string _docTitle
        -uint _currentPage
        -float _currentRotation
        -string _url
        -string _dropdownItemCheckIcon
        -string _dropdownItemDefaultIcon
        +Task OnAfterRenderAsync(bool firstRender)
        +Task Printing()
        +Task RotationChanged(int angle)
    }

    class PdfReaderJsInterop {
        +void addEventBus(el, pdfViewer, eventBus, invoke, options)
    }

    PdfReader --> PdfReaderJsInterop : uses JS interop
Loading

File-Level Changes

Change Details Files
Introduce a rotation angle parameter and state tracking on the PdfReader component.
  • Replace the existing scale-related parameter with an integer rotation parameter representing 0/90/180/270 degrees.
  • Add a private backing field to track the current rotation and keep it in sync with the public parameter in lifecycle methods.
  • Extend the component initialization options object to carry a flag indicating whether rotation change callbacks are enabled.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
Wire rotation changes between .NET and JavaScript via JS interop and PDF.js event bus.
  • On parameter changes, invoke a JavaScript "rotate" function with the current rotation value to rotate the PDF view.
  • Expose a JS-invokable RotationChanged method that forwards rotation changes to an optional OnRotationChanged callback delegate.
  • Subscribe to the PDF.js rotationchanging event in JavaScript to update thumbnail rotation CSS and, when enabled, call back into .NET with the new rotation.
  • Make the PDF.js event bus handler async to await the .NET callback invocation.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js

Assessment against linked issues

Issue Objective Addressed Explanation
#757 Add an OnRotationChanged parameter/callback to the PdfReader component that is invoked when the PDF viewer rotation changes.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The new rotation state field is declared as a float while the public CurrentRotation parameter is an int; consider making _currentRotation an int as well to avoid unnecessary type conversions and potential comparison issues.
  • The OnRotationChanged parameter returns a Task but does not follow the existing *Async naming convention (e.g., OnScaleChangedAsync); consider renaming for consistency across the component’s async callbacks.
  • Since CurrentRotation is documented as only supporting 0/90/180/270, consider validating or normalizing incoming values before passing them to the JS rotate call to prevent undefined behavior with other angles.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new rotation state field is declared as a float while the public `CurrentRotation` parameter is an int; consider making `_currentRotation` an int as well to avoid unnecessary type conversions and potential comparison issues.
- The `OnRotationChanged` parameter returns a `Task` but does not follow the existing `*Async` naming convention (e.g., `OnScaleChangedAsync`); consider renaming for consistency across the component’s async callbacks.
- Since `CurrentRotation` is documented as only supporting 0/90/180/270, consider validating or normalizing incoming values before passing them to the JS `rotate` call to prevent undefined behavior with other angles.

## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs:151` </location>
<code_context>

     private string? _docTitle;
     private uint _currentPage;
+    private float _currentRotation;
     private string? _url;
     private string? _dropdownItemCheckIcon;
</code_context>

<issue_to_address>
**issue (bug_risk):** Use a consistent integer type for rotation instead of mixing int and float.

`CurrentRotation` is an `int` with documented discrete values (0, 90, 180, 270), but the backing field is a `float`. This mismatch is unnecessary and could mask accidental non-discrete values in future changes. Please make `_currentRotation` an `int` so the type matches the public API, the value domain, and the comparisons in `OnAfterRenderAsync`.
</issue_to_address>

### Comment 2
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs:351-363` </location>
<code_context>
+    /// <param name="angle"></param>
+    /// <returns></returns>
+    [JSInvokable]
+    public async Task RotationChanged(int angle)
+    {
+        if (OnRotationChanged != null)
+        {
+            await OnRotationChanged(angle);
+        }
+    }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider updating the component’s internal rotation state when the JS callback fires.

`RotationChanged(int angle)` only calls `OnRotationChanged` and never updates `CurrentRotation` / `_currentRotation`, so the internal state can diverge from the viewer’s actual rotation when changes come from JS. If `CurrentRotation` is expected to always reflect the latest rotation, consider assigning it here as well, or clearly document that this method is notification-only and does not update state.

```suggestion
    /// <summary>
    /// 页面旋转回调方法
    /// </summary>
    /// <param name="angle">当前页面旋转角度</param>
    /// <returns></returns>
    [JSInvokable]
    public async Task RotationChanged(int angle)
    {
        // Synchronize internal rotation state with the value coming from JS
        if (CurrentRotation != angle)
        {
            CurrentRotation = angle;
        }

        if (_currentRotation != angle)
        {
            _currentRotation = angle;
        }

        if (OnRotationChanged != null)
        {
            await OnRotationChanged(angle);
        }
    }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


private string? _docTitle;
private uint _currentPage;
private float _currentRotation;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Use a consistent integer type for rotation instead of mixing int and float.

CurrentRotation is an int with documented discrete values (0, 90, 180, 270), but the backing field is a float. This mismatch is unnecessary and could mask accidental non-discrete values in future changes. Please make _currentRotation an int so the type matches the public API, the value domain, and the comparisons in OnAfterRenderAsync.

@ArgoZhang ArgoZhang merged commit 61a568c into master Nov 30, 2025
7 checks passed
@ArgoZhang ArgoZhang deleted the feat-reader branch November 30, 2025 01:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a rotation change callback feature to the PdfReader component, allowing consumers to track when the PDF rotation angle changes. The implementation follows the existing event callback pattern used for other PDF viewer events like page changes and scale changes.

  • Adds OnRotationChanged callback parameter to notify when PDF rotation changes
  • Implements JavaScript event handler for rotationchanging event with callback to C#
  • Updates initialization options to include rotation change trigger flag

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
PdfReader.razor.js Added async callback invocation for rotationchanging event when triggerRotationChanged is enabled
PdfReader.razor.cs Added OnRotationChanged parameter, RotationChanged method, CurrentRotation property with change detection, and TriggerRotationChanged option

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// 页面旋转回调方法
/// </summary>
[Parameter]
public Func<int, Task>? OnRotationChanged { get; set; }
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback parameter name 'OnRotationChanged' is inconsistent with the naming convention used by other callbacks in this class. All other callback parameters follow the 'OnXxxAsync' pattern (e.g., 'OnPagesInitAsync', 'OnPageChangedAsync', 'OnScaleChangedAsync'). This should be renamed to 'OnRotationChangedAsync' to maintain consistency.

Suggested change
public Func<int, Task>? OnRotationChanged { get; set; }
public Func<int, Task>? OnRotationChangedAsync { get; set; }

Copilot uses AI. Check for mistakes.

private string? _docTitle;
private uint _currentPage;
private float _currentRotation;
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type mismatch: The field '_currentRotation' is declared as 'float', but the 'CurrentRotation' property is 'int'. This inconsistency could lead to type conversion issues. Since rotation angles are discrete values (0, 90, 180, 270) as documented, '_currentRotation' should be 'int' to match the property type.

Suggested change
private float _currentRotation;
private int _currentRotation;

Copilot uses AI. Check for mistakes.

/// <summary>
/// 获得/设置 当前缩放倍率 默认 null 使用 100%
/// 获得/设置 当前旋转角度 默认 0 数值范围 0 90 180 270
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment describes the CurrentRotation property but appears in the diff as replacing the previous CurrentScale comment. While the comment itself is correct for CurrentRotation, verify this is intentional and that CurrentScale was truly removed. If CurrentScale was actually removed as a breaking change, this should be documented in the PR description or CHANGELOG.

Copilot uses AI. Check for mistakes.
_currentPage = CurrentPage;
await NavigateToPageAsync(_currentPage);
}
if (_currentRotation != CurrentRotation)
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Equality checks on floating point values can yield unexpected results.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(PdfReader): add OnRotationChanged parameter

2 participants