Skip to content

Remove QuestPDF — its Community licence is pay-to-use above a revenue threshold #1230

Closed

Description

@rbuergi

MeshWeaver ships dual-licensed Apache-2.0 / MIT. QuestPDF (Directory.Packages.props, 2026.7.2) is dual-licensed: the Community tier is free only below a revenue threshold; above it a paid commercial licence is required. MarkdownExportExtensions.cs:60 accepts that Community licence programmatically at mesh-builder time, and MeshWeaver.Blazor pulls it transitively, so QuestPDF ships in every deployed portal image.

The user's decision (2026-08-11) is to dereference it — not to buy a licence and not to swap in another restrictively-licensed package. PDF rendering moves to the browser via the existing Pixel/HeadlessChromiumPdfRenderer.cs (Playwright, MIT).

Footprint (verified on main)

  • src/MeshWeaver.Markdown.Export/Pdf/PdfDocumentRenderer.cs — 368 lines, 100% of the QuestPDF rendering, the only file with using QuestPDF.
  • src/MeshWeaver.Markdown.Export/Configuration/MarkdownExportExtensions.cs:60 — Settings.License = LicenseType.Community.
  • src/MeshWeaver.Markdown.Export/MeshWeaver.Markdown.Export.csproj:8 — the only PackageReference.
  • Tests using it: PdfHeaderLogoTests.cs, RendererOutputTests.cs.
  • ExportFidelity.cs, ExportFormat.cs, Branding/ExportTemplateResolver.cs mention it in comments only — no compile dependency.

Why it is not a one-line deletion

Fork point Templates/ExportPdf.csx:126: Pixel fidelity goes to Chromium (Deck only); ExportPdf.csx:193-196 warns and falls back to QuestPDF for every non-Deck node. Per PixelFaithfulExport.md:70 the Chromium path today has no cover page, no TOC, no running header/footer and no page-break rules — those exist only in PdfDocumentRenderer.cs. README.md:23 also states headless Chromium is deliberately not shipped in the portal image.

So the deletion must land with the document-export migration onto Chromium, otherwise it is a functional regression rather than a cleanup.

Done when

  • Markdown→PDF, cover page, TOC and running header/footer render through the browser path
  • PdfDocumentRenderer.cs, the licence line and the PackageReference are deleted
  • PdfHeaderLogoTests / RendererOutputTests assert the same behaviour against browser-rendered output (do not weaken them)
  • <PackageVersion Include="QuestPDF" …> removed from Directory.Packages.props
  • the questpdf entry is deleted from EXCEPTIONS in .github/scripts/check-licenses.py — the licence gate then enforces this permanently

Activity

  1. rbuergi commented on Aug 11, 2026

    @rbuergi
    ContributorAuthor

    Hard prerequisite: Chromium is not in the deployed image

    Confirmed while auditing dependency licences (#1233): deploy/base-images/portal-ai/Dockerfile — the base of memex-portal-ai per main-cd.yml — does not include a browser, and that is a stated design choice rather than an oversight.

    That changes this issue's shape:

    1. The "let the browser render it" route cannot be the production PDF path until the base image gains Chromium. That is a deliberate deployment change with image-size, attack-surface and build-time consequences, and it deserves its own decision — it is not a side effect of a licence cleanup.
    2. Removing QuestPDF before that lands would leave the portal with no PDF renderer at all. That would be strictly worse than the licence exposure it removes.
    3. There is also no HTML→DOCX converter in the repo, so DOCX has no browser-based answer either — worth settling in the same decision.

    Sequencing

    #1222  →  #1234  →  base-image decision on shipping Chromium  →  QuestPDF removal (this issue)
    

    #1234 fixed the real defect underneath this at its root: Ast/DocumentBuilder.cs built its own Markdig pipeline that omitted LayoutAreaMarkdownExtension, so @@(…) parsed as a paragraph and printed as raw source; the pixel path emitted the empty layout-area anchor, but its print document is file:// under default-src 'none' with --host-resolver-rules=MAP * ~NOTFOUND, so the browser could never fill it and printed blank. It also lifted the resolver/renderer/table-sizer/sanitizer out of Email/ into a format-neutral Html/.

    This issue stays open and blocked on that chain. Meanwhile QuestPDF is carried as a dated, reasoned exception in EXCEPTIONS in .github/scripts/check-licenses.py (added by #1233), so the licence gate enforces everything else today while this stays visible instead of silently permanent. Closing this issue includes deleting that entry.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions