Skip to content

feat(PdfReader): add Document properties Function#751

Merged
ArgoZhang merged 5 commits intomasterfrom
refactor-pdf-worker
Nov 29, 2025
Merged

feat(PdfReader): add Document properties Function#751
ArgoZhang merged 5 commits intomasterfrom
refactor-pdf-worker

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Nov 29, 2025

Link issues

fixes #750

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 a document properties dialog to the PDF reader and wire it to the toolbar while aligning worker configuration and minor UI behavior.

New Features:

  • Introduce a document properties dialog in PdfReader that displays file metadata such as name, size, creation date, producer, version, page count, and page size.
  • Add a toolbar dropdown action to open and close the document properties dialog within the PDF viewer.

Enhancements:

  • Update the pdf.js worker source path to load the worker from the PdfReader static content location.
  • Parse and format PDF metadata dates and file sizes on the client to present user-friendly values in the properties dialog.
  • Adjust toolbar divider markup and minor DOM/class handling in the viewer, including cleaning up event handlers on dispose.

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

sourcery-ai Bot commented Nov 29, 2025

Reviewer's Guide

Implements a document properties dialog for the PdfReader component, wiring pdf.js metadata into a new UI overlay, updating toolbar interactions, and making small robustness fixes to scaling and printing behavior.

Sequence diagram for loading PDF metadata into the document properties dialog

sequenceDiagram
    participant PdfReaderInit as PdfReader_init
    participant PdfJs as pdfjsLib
    participant PdfDocument as pdfDocument
    participant PdfViewer as pdfViewer
    participant Dom as Document_DOM

    PdfReaderInit->>PdfJs: getDocument(options)
    PdfJs-->>PdfReaderInit: loadingTask
    PdfReaderInit->>PdfJs: loadingTask.promise
    PdfJs-->>PdfReaderInit: pdfDocument
    PdfReaderInit->>PdfViewer: setDocument(pdfDocument)

    PdfReaderInit->>PdfDocument: getMetadata()
    PdfDocument-->>PdfReaderInit: metadata

    PdfReaderInit->>Dom: loadMetadata(el, pdfViewer, metadata)
    activate Dom
    Dom->>Dom: query .bb-view-pdf-dialog-* elements
    Dom->>Dom: getFilesize(metadata)
    Dom-->>Dom: formatted file size
    Dom->>Dom: parsePdfDate(metadata.info.CreationDate)
    Dom-->>Dom: Date or null
    Dom->>PdfViewer: read pagesCount
    Dom->>PdfDocument: getPage(pdfViewer.currentPageNumber)
    PdfDocument-->>Dom: page
    Dom->>Dom: page.getViewport(scale: 1)
    Dom-->>Dom: viewport (width, height)
    Dom->>Dom: update dialog textContent fields
    deactivate Dom
Loading

Sequence diagram for opening and closing the document properties dialog

sequenceDiagram
    actor User
    participant Toolbar as PdfReader_toolbar
    participant EventHandler as EventHandler_module
    participant Dom as Document_DOM

    User->>Toolbar: click .dropdown-item-doc
    Toolbar->>EventHandler: delegated click handler
    EventHandler->>Dom: query .bb-view-pdf-info
    Dom-->>EventHandler: dialog element
    EventHandler->>Dom: dialog.classList.add(show)

    User->>Dom: click .btn-close-doc
    Dom->>EventHandler: click handler for close button
    EventHandler->>Dom: query .bb-view-pdf-info
    Dom-->>EventHandler: dialog element
    EventHandler->>Dom: dialog.classList.remove(show)
Loading

Flow diagram for parsing a PDF date string (parsePdfDate)

flowchart TD
    A["Start parsePdfDate(pdfDateString)"] --> B{"Is pdfDateString nonempty string?"}
    B -- No --> Z["Return null"]
    B -- Yes --> C{"Starts with D:?"}
    C -- Yes --> D["Remove leading D:"]
    C -- No --> E["Use original string"]
    D --> F["dateStr = trimmed value"]
    E --> F
    F --> G{"Match dateStr against regex"}
    G -- No --> Z
    G -- Yes --> H["Extract year, month, day, hours, minutes, seconds, timezoneSign, timezoneHours, timezoneMinutes"]
    H --> I["Create Date with local year, month-1, day, hours, minutes, seconds"]
    I --> J{"timezoneSign is Z/z?"}
    J -- Yes --> K["Compute UTC time with Date.UTC and set date time"]
    J -- No --> L{"timezoneSign is + or -?"}
    L -- No --> Y["Return date"]
    L -- Yes --> M["Compute totalOffsetMinutes from timezoneHours and timezoneMinutes"]
    M --> N{"timezoneSign is +?"}
    N -- Yes --> O["date.setMinutes(date.getMinutes() - totalOffsetMinutes)"]
    N -- No --> P["date.setMinutes(date.getMinutes() + totalOffsetMinutes)"]
    O --> Y
    P --> Y
    K --> Y
    Y["Return Date object"] --> Q["End"]
    Z --> Q
Loading

File-Level Changes

Change Details Files
Wire pdf.js worker and document metadata retrieval into the PdfReader viewer lifecycle.
  • Update pdf.js workerSrc to point to the packaged worker under the component content path.
  • After loading a PDF document, call getMetadata on the pdfDocument and pass the result into a new metadata initialization helper.
  • Compute and populate derived metadata values such as formatted file size and parsed creation date using helper functions.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js
Add and style a document properties dialog overlay and hook it to the toolbar item and close button.
  • Introduce a new .bb-view-pdf-info overlay markup structure in the PdfReader Razor file, with fields for filename, size, title, author, subject, keywords, dates, application, producer, version, page count, page size, and fast web view indicator, plus a localized Close button.
  • Add CSS for the document properties overlay, backdrop, dialog layout, labels, and close button spacing, including show/hide behavior via a .show class.
  • Attach click handlers to the toolbar document properties dropdown item to show the dialog and to the dialog close button to hide it, and clean up the close button handler in dispose.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.css
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js
Refine zoom display, print iframe setup, and toolbar divider markup for better semantics and behavior.
  • Fix scale percentage formatting to use Math.round with a single argument, removing the unused second parameter in both updateScaleValue and updateScale.
  • Change iframe class assignment in printPdf to use classList.add instead of overwriting classList.
  • Replace the Divider component usage with equivalent divider/divider-mask markup for consistency with the new dialog styling.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js
src/components/BootstrapBlazor.PdfReader/PdfReader.razor

Assessment against linked issues

Issue Objective Addressed Explanation
#750 Add a document properties function to the PdfReader component that displays PDF metadata (e.g., file name, size, title, author, dates, page info) via the existing toolbar control.

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

@ArgoZhang ArgoZhang merged commit 13369cf into master Nov 29, 2025
3 of 4 checks passed
@ArgoZhang ArgoZhang deleted the refactor-pdf-worker branch November 29, 2025 02:35
@ArgoZhang ArgoZhang restored the refactor-pdf-worker branch November 29, 2025 02:36
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 and found some issues that need to be addressed.

  • In loadMetadata you reuse the .bb-view-pdf-dialog-title class for both the dialog heading and the metadata value and then select it via querySelector, which will only target the first element and may overwrite the title instead of the metadata field; consider using distinct class names or more specific selectors for each role.
  • loadMetadata assumes metadata, metadata.info, and properties like CreationDate, Creator, Producer, and PDFFormatVersion always exist, and getFilesize assumes metadata.contentLength is defined and small enough to match existing branches; it would be safer to guard these accesses and provide sensible fallbacks when values are missing or unexpected.
  • There is a lingering console.log(metadata); and the dropdown toolbar click handler for .dropdown-item-doc is not cleaned up in dispose, which can lead to noisy logs and potential event handler leaks if the component is initialized multiple times; consider removing the log and unregistering the toolbar handler similarly to the close button.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `loadMetadata` you reuse the `.bb-view-pdf-dialog-title` class for both the dialog heading and the metadata value and then select it via `querySelector`, which will only target the first element and may overwrite the title instead of the metadata field; consider using distinct class names or more specific selectors for each role.
- `loadMetadata` assumes `metadata`, `metadata.info`, and properties like `CreationDate`, `Creator`, `Producer`, and `PDFFormatVersion` always exist, and `getFilesize` assumes `metadata.contentLength` is defined and small enough to match existing branches; it would be safer to guard these accesses and provide sensible fallbacks when values are missing or unexpected.
- There is a lingering `console.log(metadata);` and the dropdown toolbar click handler for `.dropdown-item-doc` is not cleaned up in `dispose`, which can lead to noisy logs and potential event handler leaks if the component is initialized multiple times; consider removing the log and unregistering the toolbar handler similarly to the close button.

## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:111-120` </location>
<code_context>
     return pdfViewer;
 }

+const loadMetadata = (el, pdfViewer, metadata) => {
+    console.log(metadata);
+
+    const filename = el.querySelector('.bb-view-pdf-dialog-filename');
+    const docTitle = el.querySelector('.bb-view-subject');
+    filename.textContent = docTitle.textContent;
+
+    const filesize = el.querySelector('.bb-view-pdf-dialog-filesize');
+    filesize.textContent = getFilesize(metadata);
+
+    const title = el.querySelector('.bb-view-pdf-dialog-title');
+    const author = el.querySelector('.bb-view-pdf-dialog-author');
+    const subject = el.querySelector('.bb-view-pdf-dialog-subject');
+    const keywords = el.querySelector('.bb-view-pdf-dialog-keywords');
+    const created = el.querySelector('.bb-view-pdf-dialog-created');
+    created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();
</code_context>

<issue_to_address>
**issue (bug_risk):** Multiple queried metadata elements are never populated, so the dialog will remain empty for those fields.

In `loadMetadata`, `title`, `author`, `subject`, and `keywords` are queried but never assigned from `metadata`, so those dialog rows will always be blank even when the PDF provides values. Please populate them from `metadata` (e.g. `title.textContent = metadata.info.Title || ''`) so all available metadata is shown.
</issue_to_address>

### Comment 2
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:125-126` </location>
<code_context>
+    const author = el.querySelector('.bb-view-pdf-dialog-author');
+    const subject = el.querySelector('.bb-view-pdf-dialog-subject');
+    const keywords = el.querySelector('.bb-view-pdf-dialog-keywords');
+    const created = el.querySelector('.bb-view-pdf-dialog-created');
+    created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();
+
+    const modified = el.querySelector('.bb-view-pdf-dialog-modified');
</code_context>

<issue_to_address>
**issue:** Direct access to `metadata.info.*` assumes all properties exist and can throw when metadata is missing.

In `loadMetadata`, please guard these accesses with `metadata && metadata.info` and use optional chaining or fallbacks when assigning to `textContent`, e.g. `created.textContent = metadata.info?.CreationDate ? parsePdfDate(metadata.info.CreationDate)?.toLocaleString() : ''` and similar for the other fields.
</issue_to_address>

### Comment 3
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:128-137` </location>
<code_context>
+    const modified = el.querySelector('.bb-view-pdf-dialog-modified');
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The `modified` and `webview` elements are queried but never used, leaving the dialog fields inconsistent.

`.bb-view-pdf-dialog-modified` and `.bb-view-pdf-dialog-webview` are queried, but `modified.textContent` is never set and `webview` is never used (Razor currently hardcodes `No`). This leaves “Modified” always blank and makes the `webview` query dead code. Either populate `modified` (e.g. from `metadata.info.ModDate` via `parsePdfDate`) and drive `webview` from real document data, or remove these queries to avoid inconsistent UI and unused elements.

Suggested implementation:

```javascript
    const created = el.querySelector('.bb-view-pdf-dialog-created');
    created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();

    const modified = el.querySelector('.bb-view-pdf-dialog-modified');
    modified.textContent = parsePdfDate(metadata.info.ModDate)?.toLocaleString();

    const application = el.querySelector('.bb-view-pdf-dialog-application');

```

1. Locate where `.bb-view-pdf-dialog-webview` is queried in `PdfReader.razor.js` and either:
   - Bind it to a real data source (e.g. a boolean or flag in `metadata` that indicates whether the document was viewed via web), or
   - Remove the query and any associated unused variable if no such data is available yet.
2. Ensure the Razor markup for the “Modified” field is present and correctly bound to `.bb-view-pdf-dialog-modified` so the populated value is visible.
</issue_to_address>

### Comment 4
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:202-211` </location>
<code_context>
+    return date;
+}
+
+const getFilesize = metadata => {
+    const length = metadata.contentLength;
+    if (length < 1024) {
+        return `${Math.round(length)}B`;
+    }
+    else if (length < 1024 * 1024) {
+        return `${Math.round(length / 1024)}KB`;
+    }
+    else if (length < 1024 * 1024 * 1024) {
+        return `${length / 1024 / 1024}MB`;
+    }
+    else if (length < 1024 * 1024 * 1024 * 1024) {
+        return `${length / 1024 / 1024 / 1024}GB`;
+    }
</code_context>

<issue_to_address>
**issue (bug_risk):** `getFilesize` can return `undefined` for large files and uses inconsistent rounding between units.

For `metadata.contentLength >= 1024 * 1024 * 1024 * 1024`, the function returns nothing, so the UI will display `undefined`. Also, MB/GB values are not rounded, unlike B/KB, which may produce long decimals. Please add a final fallback branch and standardize rounding across units (e.g. `return `${(length / 1024 / 1024).toFixed(1)} MB`;`, and similarly for sizes ≥ 1 TB).
</issue_to_address>

### Comment 5
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:446-447` </location>
<code_context>
+        }
+    });
+
+    const closeButton = el.querySelector(".btn-close-doc");
+    EventHandler.on(closeButton, 'click', e => {
+        const dialog = el.querySelector(".bb-view-pdf-info");
+        if (dialog) {
</code_context>

<issue_to_address>
**issue (bug_risk):** `EventHandler.on` is called unconditionally on `closeButton` and can fail if the button is missing.

`dispose` already guards `closeButton` with an `if (closeButton)`, but the event registration path does not and will throw if the button is absent (e.g., due to markup or conditional rendering changes). Wrapping the `EventHandler.on(closeButton, 'click', ...)` call in the same null check would avoid this and keep the behavior consistent.
</issue_to_address>

### Comment 6
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor:104-113` </location>
<code_context>
+    <div class="bb-view-pdf-info">
+        <div class="bb-view-pdf-backdrop"></div>
+        <div class="bb-view-pdf-dialog">
+            <div class="bb-view-pdf-dialog-title mb-3">Document properties</div>
+            <div class="bb-view-pdf-dialog-item">
+                <div class="bb-view-pdf-dialog-label">File name:</div>
+                <div class="bb-view-pdf-dialog-filename"></div>
+            </div>
+            <div class="bb-view-pdf-dialog-item">
+                <div class="bb-view-pdf-dialog-label">File size:</div>
+                <div class="bb-view-pdf-dialog-filesize"></div>
+            </div>
+            <div class="divider">
+                <div class="divider-mask"></div>
+            </div>
+            <div class="bb-view-pdf-dialog-item">
+                <div class="bb-view-pdf-dialog-label">Title:</div>
+                <div class="bb-view-pdf-dialog-title"></div>
+            </div>
+            <div class="bb-view-pdf-dialog-item">
</code_context>

<issue_to_address>
**issue (bug_risk):** The same CSS class is used for the dialog header and the PDF title field, which breaks the JS metadata binding.

`loadMetadata` uses `el.querySelector('.bb-view-pdf-dialog-title')` and assumes it points to the Title metadata field, but in the markup that class is applied to both the header and the title value row. Because `querySelector` returns the first match (the header), the title cell never gets populated. Please give the title value a unique class (e.g. `.bb-view-pdf-dialog-title-value`) and update the JS selector to match.
</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.

Comment on lines +111 to +120
const loadMetadata = (el, pdfViewer, metadata) => {
console.log(metadata);

const filename = el.querySelector('.bb-view-pdf-dialog-filename');
const docTitle = el.querySelector('.bb-view-subject');
filename.textContent = docTitle.textContent;

const filesize = el.querySelector('.bb-view-pdf-dialog-filesize');
filesize.textContent = getFilesize(metadata);

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): Multiple queried metadata elements are never populated, so the dialog will remain empty for those fields.

In loadMetadata, title, author, subject, and keywords are queried but never assigned from metadata, so those dialog rows will always be blank even when the PDF provides values. Please populate them from metadata (e.g. title.textContent = metadata.info.Title || '') so all available metadata is shown.

Comment on lines +125 to +126
const created = el.querySelector('.bb-view-pdf-dialog-created');
created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();
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: Direct access to metadata.info.* assumes all properties exist and can throw when metadata is missing.

In loadMetadata, please guard these accesses with metadata && metadata.info and use optional chaining or fallbacks when assigning to textContent, e.g. created.textContent = metadata.info?.CreationDate ? parsePdfDate(metadata.info.CreationDate)?.toLocaleString() : '' and similar for the other fields.

Comment on lines +128 to +137
const modified = el.querySelector('.bb-view-pdf-dialog-modified');

const application = el.querySelector('.bb-view-pdf-dialog-application');
application.textContent = metadata.info.Creator;

const producer = el.querySelector('.bb-view-pdf-dialog-producer');
producer.textContent = metadata.info.Producer;

const version = el.querySelector('.bb-view-pdf-dialog-version');
version.textContent = metadata.info.PDFFormatVersion;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): The modified and webview elements are queried but never used, leaving the dialog fields inconsistent.

.bb-view-pdf-dialog-modified and .bb-view-pdf-dialog-webview are queried, but modified.textContent is never set and webview is never used (Razor currently hardcodes No). This leaves “Modified” always blank and makes the webview query dead code. Either populate modified (e.g. from metadata.info.ModDate via parsePdfDate) and drive webview from real document data, or remove these queries to avoid inconsistent UI and unused elements.

Suggested implementation:

    const created = el.querySelector('.bb-view-pdf-dialog-created');
    created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();

    const modified = el.querySelector('.bb-view-pdf-dialog-modified');
    modified.textContent = parsePdfDate(metadata.info.ModDate)?.toLocaleString();

    const application = el.querySelector('.bb-view-pdf-dialog-application');
  1. Locate where .bb-view-pdf-dialog-webview is queried in PdfReader.razor.js and either:
    • Bind it to a real data source (e.g. a boolean or flag in metadata that indicates whether the document was viewed via web), or
    • Remove the query and any associated unused variable if no such data is available yet.
  2. Ensure the Razor markup for the “Modified” field is present and correctly bound to .bb-view-pdf-dialog-modified so the populated value is visible.

Comment on lines +202 to +211
const getFilesize = metadata => {
const length = metadata.contentLength;
if (length < 1024) {
return `${Math.round(length)}B`;
}
else if (length < 1024 * 1024) {
return `${Math.round(length / 1024)}KB`;
}
else if (length < 1024 * 1024 * 1024) {
return `${length / 1024 / 1024}MB`;
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): getFilesize can return undefined for large files and uses inconsistent rounding between units.

For metadata.contentLength >= 1024 * 1024 * 1024 * 1024, the function returns nothing, so the UI will display undefined. Also, MB/GB values are not rounded, unlike B/KB, which may produce long decimals. Please add a final fallback branch and standardize rounding across units (e.g. return ${(length / 1024 / 1024).toFixed(1)} MB;, and similarly for sizes ≥ 1 TB).

Comment on lines +104 to +113
<div class="bb-view-pdf-dialog-title mb-3">Document properties</div>
<div class="bb-view-pdf-dialog-item">
<div class="bb-view-pdf-dialog-label">File name:</div>
<div class="bb-view-pdf-dialog-filename"></div>
</div>
<div class="bb-view-pdf-dialog-item">
<div class="bb-view-pdf-dialog-label">File size:</div>
<div class="bb-view-pdf-dialog-filesize"></div>
</div>
<div class="divider">
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): The same CSS class is used for the dialog header and the PDF title field, which breaks the JS metadata binding.

loadMetadata uses el.querySelector('.bb-view-pdf-dialog-title') and assumes it points to the Title metadata field, but in the markup that class is applied to both the header and the title value row. Because querySelector returns the first match (the header), the title cell never gets populated. Please give the title value a unique class (e.g. .bb-view-pdf-dialog-title-value) and update the JS selector to match.

@ArgoZhang ArgoZhang deleted the refactor-pdf-worker branch November 29, 2025 02:40
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 document properties functionality to the PdfReader component, allowing users to view metadata information about loaded PDF files. The implementation includes a modal dialog that displays file information, document metadata, and PDF-specific properties.

Key Changes:

  • Added a document properties dialog with metadata display functionality including file name, size, title, author, creation date, PDF version, and page information
  • Implemented PDF date parsing and file size formatting utilities
  • Fixed several code quality issues including Math.round parameter usage and classList assignment

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
PdfReader.razor.js Added loadMetadata function, parsePdfDate utility, getFilesize formatter, and event handlers for the document properties dialog; fixed worker path and code quality issues
PdfReader.razor Added HTML structure for the document properties modal dialog with metadata fields and close button
PdfReader.razor.css Added styling for the document properties dialog, backdrop, and layout
en.json Added English localization for "CloseButtonText"
zh.json Added Chinese localization for "CloseButtonText"
BootstrapBlazor.PdfReader.csproj Bumped version from 10.0.6 to 10.0.7
Comments suppressed due to low confidence (10)

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

  • Unused variable title.
    const title = el.querySelector('.bb-view-pdf-dialog-title');

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

  • Unused variable author.
    const author = el.querySelector('.bb-view-pdf-dialog-author');

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

  • Unused variable subject.
    const subject = el.querySelector('.bb-view-pdf-dialog-subject');

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

  • Unused variable keywords.
    const keywords = el.querySelector('.bb-view-pdf-dialog-keywords');

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

  • Unused variable modified.
    const modified = el.querySelector('.bb-view-pdf-dialog-modified');

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

  • Unused variable webview.
    const webview = el.querySelector('.bb-view-pdf-dialog-webview');

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

  • Unused variable scaleEl.
    const scaleEl = el.querySelector(".bb-view-scale-input");

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

  • The initial value of v is unused, since it is always overwritten.
    let v = 100;

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

  • Avoid automated semicolon insertion (94% of all statements in the enclosing function have an explicit semicolon).
    })

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

  • Avoid automated semicolon insertion (91% of all statements in the enclosing function have an explicit semicolon).
    delete el.widths

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

producer.textContent = metadata.info.Producer;

const version = el.querySelector('.bb-view-pdf-dialog-version');
version.textContent = metadata.info.PDFFormatVersion;
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

Potential null reference error if metadata.info.PDFFormatVersion is undefined. Add a null check or use optional chaining: version.textContent = metadata.info.PDFFormatVersion || ''

Suggested change
version.textContent = metadata.info.PDFFormatVersion;
version.textContent = metadata.info?.PDFFormatVersion || '';

Copilot uses AI. Check for mistakes.
return `${Math.round(length / 1024)}KB`;
}
else if (length < 1024 * 1024 * 1024) {
return `${length / 1024 / 1024}MB`;
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The MB calculation should be rounded for consistency with KB formatting. Consider: return \${Math.round(length / 1024 / 1024)}MB``

Suggested change
return `${length / 1024 / 1024}MB`;
return `${Math.round(length / 1024 / 1024)}MB`;

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +214
return `${length / 1024 / 1024}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${length / 1024 / 1024 / 1024}GB`;
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The GB calculation should be rounded for consistency with KB and MB formatting. Consider: return \${Math.round(length / 1024 / 1024 / 1024)}GB``

Suggested change
return `${length / 1024 / 1024}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${length / 1024 / 1024 / 1024}GB`;
return `${Math.round(length / 1024 / 1024)}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${Math.round(length / 1024 / 1024 / 1024)}GB`;

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +215
return `${length / 1024 / 1024}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${length / 1024 / 1024 / 1024}GB`;
}
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The function doesn't have an explicit return statement for the GB case and beyond. Add an explicit return statement to ensure the function always returns a value, or add an else clause with a fallback.

Suggested change
return `${length / 1024 / 1024}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${length / 1024 / 1024 / 1024}GB`;
}
return `${Math.round(length / 1024 / 1024)}MB`;
}
else if (length < 1024 * 1024 * 1024 * 1024) {
return `${Math.round(length / 1024 / 1024 / 1024)}GB`;
}
else {
return `${Math.round(length / 1024 / 1024 / 1024 / 1024)}TB`;
}

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +118
<div class="bb-view-pdf-dialog-title mb-3">Document properties</div>
<div class="bb-view-pdf-dialog-item">
<div class="bb-view-pdf-dialog-label">File name:</div>
<div class="bb-view-pdf-dialog-filename"></div>
</div>
<div class="bb-view-pdf-dialog-item">
<div class="bb-view-pdf-dialog-label">File size:</div>
<div class="bb-view-pdf-dialog-filesize"></div>
</div>
<div class="divider">
<div class="divider-mask"></div>
</div>
<div class="bb-view-pdf-dialog-item">
<div class="bb-view-pdf-dialog-label">Title:</div>
<div class="bb-view-pdf-dialog-title"></div>
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The class name bb-view-pdf-dialog-title is used for both the dialog header (line 104) and a data field for the document title (line 118). This creates ambiguity and makes it impossible to target these elements separately. Consider renaming one of them, e.g., use bb-view-pdf-dialog-header for the dialog header at line 104.

Copilot uses AI. Check for mistakes.
size.textContent = `${(viewport.width / 72).toFixed(2)} * ${(viewport.height / 72).toFixed(2)} in (portrait)`;
});

const webview = el.querySelector('.bb-view-pdf-dialog-webview');
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

Variable webview is declared but never used. It should be populated with fast web view information or removed if not needed.

Suggested change
const webview = el.querySelector('.bb-view-pdf-dialog-webview');

Copilot uses AI. Check for mistakes.
const subject = el.querySelector('.bb-view-pdf-dialog-subject');
const keywords = el.querySelector('.bb-view-pdf-dialog-keywords');
const created = el.querySelector('.bb-view-pdf-dialog-created');
created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The code doesn't handle cases where metadata.info.CreationDate might be undefined or null. Consider adding a null check or providing a fallback value to avoid displaying "undefined" or causing an error: created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString() || 'N/A'

Suggested change
created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString();
created.textContent = parsePdfDate(metadata.info.CreationDate)?.toLocaleString() || 'N/A';

Copilot uses AI. Check for mistakes.
const modified = el.querySelector('.bb-view-pdf-dialog-modified');

const application = el.querySelector('.bb-view-pdf-dialog-application');
application.textContent = metadata.info.Creator;
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

Potential null reference error if metadata.info.Creator is undefined. Add a null check or use optional chaining: application.textContent = metadata.info.Creator || ''

Suggested change
application.textContent = metadata.info.Creator;
application.textContent = metadata.info?.Creator || '';

Copilot uses AI. Check for mistakes.
application.textContent = metadata.info.Creator;

const producer = el.querySelector('.bb-view-pdf-dialog-producer');
producer.textContent = metadata.info.Producer;
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

Potential null reference error if metadata.info.Producer is undefined. Add a null check or use optional chaining: producer.textContent = metadata.info.Producer || ''

Suggested change
producer.textContent = metadata.info.Producer;
producer.textContent = metadata.info?.Producer || '';

Copilot uses AI. Check for mistakes.
}

const loadMetadata = (el, pdfViewer, metadata) => {
console.log(metadata);
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

Remove the console.log statement before merging to production. Debug logging should not be left in production code.

Suggested change
console.log(metadata);

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 Document properties Function

2 participants