Skip to content

Normalize book pages before spreadsheet export (BL-16518)#8074

Draft
StephenMcConnel wants to merge 2 commits into
masterfrom
BL-16518-SSExportNotNormalized
Draft

Normalize book pages before spreadsheet export (BL-16518)#8074
StephenMcConnel wants to merge 2 commits into
masterfrom
BL-16518-SSExportNotNormalized

Conversation

@StephenMcConnel

@StephenMcConnel StephenMcConnel commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What

Spreadsheet export previously read the raw saved .htm directly, so it could miss normalizations that only happen when a page is visited in edit mode — notably the browser-side per-page fix-ups (wrapping bare text in <p>, trimming trailing <br>, canvas-element migration) that plain C# does not do.

ExportToSpreadsheet now runs BookProcessor.ProcessBook (BringBookUpToDate plus the off-screen per-page browser pass, then Save) on a fresh Book instance before reading the DOM, so the exported content matches what the user would see and save after visiting each page in the editor.

Details

  • Gated on book.IsSaveable, matching edit-mode semantics: a read-only or not-checked-out Team Collection book is left as-is (raw-disk read, no mutation).
  • The normalization runs inside the export progress dialog's background worker (via a getDom delegate) so the several-seconds-per-book browser pass does not freeze the UI.
  • Because ProcessBook rewrites the .htm on disk, the in-memory selected book is reloaded from disk when the dialog closes, mirroring the spreadsheet import path, so a later editor save cannot clobber the result.

Testing

  • Build clean; 384/384 spreadsheet C# unit tests pass.
  • Validated end-to-end in the running app: created a book from the "The Moon and the Cap" sample shell, injected un-normalized markup (bare text + trailing <br/>) into a content page's .htm, exported, and confirmed the .htm was re-normalized to <p>…</p> (br removed) and the exported .xlsx reflected the normalized text.

Scope: GUI export path only. The CLI spreadsheetExport command still reads the raw .htm (would need the full ApplicationContainer/BookServer/WebView2 stack).

Ref: https://issues.bloomlibrary.org/youtrack/issue/BL-16518

Devin review


This change is Reviewable

Spreadsheet export previously read the raw saved .htm directly, so it could
miss normalizations that only happen when a page is visited in edit mode --
notably the browser-side per-page fix-ups (e.g. wrapping bare text in <p>,
trimming trailing <br>, canvas-element migration) that plain C# does not do.

ExportToSpreadsheet now runs BookProcessor.ProcessBook (BringBookUpToDate plus
the off-screen per-page browser pass, then Save) on a fresh Book instance
before reading the DOM, so the exported content matches what the user would
see and save after visiting each page in the editor. This is gated on
book.IsSaveable, matching edit-mode semantics: a read-only or not-checked-out
Team Collection book is left as-is (raw-disk read, no mutation).

The normalization runs inside the export progress dialog's background worker
(via a getDom delegate) so the several-seconds-per-book browser pass does not
freeze the UI. Because ProcessBook rewrites the .htm on disk, the in-memory
selected book is reloaded from disk when the dialog closes, mirroring the
spreadsheet import path, so a later editor save cannot clobber the result.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR normalizes book pages (paragraph wrapping, canvas-element migration, trailing <br> removal) before spreadsheet export by running BookProcessor.ProcessBook inside the progress dialog's background worker, so exported content matches what the editor would produce after visiting each page.

  • ExportToFolderWithProgressAsync now accepts a getDomAndFolder delegate instead of a pre-resolved HtmlDom, deferring potentially slow per-page browser work to the background thread and returning the post-rename folder path alongside the DOM.
  • A reconcileSelectedBook callback (mirroring the import path) reloads the in-memory book from disk when the dialog closes, so a subsequent editor save cannot clobber the normalized result; the reload is gated on book.IsSaveable matching the processing guard used during export.
  • Normalization is skipped for read-only or non-checked-out Team Collection books, matching edit-mode semantics.

Important Files Changed

Filename Overview
src/BloomExe/Spreadsheet/SpreadsheetApi.cs Refactors export to normalize book pages before reading the DOM: adds BookServer dependency, builds getDomAndFolder and reconcileSelectedBook closures that run ProcessBook on the background thread and reload the in-memory book on dialog close; threading model, IsSaveable guard, and rename handling all look correct.
src/BloomExe/Spreadsheet/SpreadsheetExporter.cs ExportToFolderWithProgressAsync signature changed to accept a getDomAndFolder delegate (instead of pre-resolved DOM+path) and an optional doWhenDialogCloses callback; the delegate is invoked on the background worker so slow processing doesn't freeze the UI. Minimal, clean change.

Reviews (2): Last reviewed commit: "BL-16518: handle folder rename when norm..." | Re-trigger Greptile

BookProcessor.ProcessBook saves the book, which can rename its folder and
.htm to match the title. The export code read the DOM and images from paths
captured before processing, so a rename would make it read a stale path
(export throws / images missing), and the post-export reload of the selected
book would target the old folder.

The getDom delegate now returns the book folder alongside the DOM, both taken
from the fresh Book's post-processing location (GetPathHtmlFile / FolderPath),
and the reconcile step reloads the selected book pointing at the possibly-new
folder (Book.ReloadFromDisk(renamedTo)). Found in self-review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@StephenMcConnel

Copy link
Copy Markdown
Contributor Author

[Claude Opus 4.8] Consulted Devin up to 1233bb3 — review complete, no Bug or Investigate findings. Devin's summary correctly describes the change (normalize via BookProcessor.ProcessBook before reading the export DOM, gated on IsSaveable, with folder-rename reconciliation). Greptile and CI are also green.

@hatton

hatton commented Jul 17, 2026

Copy link
Copy Markdown
Member

This is going to be REALLY slow...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants